Opened 10 years ago

Closed 10 years ago

#22466 closed Bug (fixed)

'ManyToOneRel' object has no attribute 'rel'

Reported by: Takis Issaris Owned by: Anssi Kääriäinen
Component: Database layer (models, ORM) Version: 1.7-beta-2
Severity: Release blocker Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Simon Charette)

I just installed Django 1.7b1 in my development virtualenv where I normally use Django 1.6.1. After fixing several expected things (mimetype, ugettext), I got an error which I could not find a reference to in the release notes:
AttributeError at /orders/price/ 'ManyToOneRel' object has no attribute 'rel'

Request Method: POST
Request URL: http://192.168.3.200:8001/orders/price/
Django Version: 1.7b1
Python Executable: /home/ic/adk/dj/bin/python
Python Version: 2.7.3

Python Path: ['/home/ic/adk/dj/adkwoningdev', '/home/ic/adk/dj/lib/python2.7/site-packages/adkw_calctable-0.1-py2.7.egg', '/home/ic/adk/dj/lib/python2.7/site-packages/adkw_portsectionheights-0.1-py2.7.egg', '/home/ic/adk/dj/lib/python27.zip', '/home/ic/adk/dj/lib/python2.7', '/home/ic/adk/dj/lib/python2.7/plat-linux2', '/home/ic/adk/dj/lib/python2.7/lib-tk', '/home/ic/adk/dj/lib/python2.7/lib-old', '/home/ic/adk/dj/lib/python2.7/lib-dynload', '/opt/python-2.7.3/lib/python2.7', '/opt/python-2.7.3/lib/python2.7/plat-linux2', '/opt/python-2.7.3/lib/python2.7/lib-tk', '/home/ic/adk/dj/lib/python2.7/site-packages', '/home/ic/adk/dj/lib/python2.7/site-packages/PIL']

Server time: Thu, 17 Apr 2014 12:10:24 +0200

Installed Applications: ('django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', 'south', 'orders', 'siunits', 'portsections', 'springcalc', 'ral', 'printer', 'springorders', 'glassorders', 'suppliers', 'scanner', 'statistics', 'django_extensions', 'panels', 'tracks', 'drive', 'person', 'discounts', 'production', 'productionplanning', 'nonstdports', 'unstructuredorders', 'adkapp5', 'loading', 'paints', 'deliverynote', 'mail', 'download', 'experiments')

Installed Middleware: ('django.middleware.gzip.GZipMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'mymid.CustomMiddleware')

Traceback: File "/home/ic/adk/dj/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response 113. response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/home/ic/adk/dj/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view 22. return view_func(request, *args, **kwargs) File "/home/ic/adk/dj/adkwoningdev/orders/views.py" in price 161. price = order.get_price_estimate() File "/home/ic/adk/dj/adkwoningdev/orders/models.py" in get_price_estimate 1473. return self.count * self.process_order_items(self.price_item_discount) File "/home/ic/adk/dj/adkwoningdev/orders/models.py" in process_order_items 1198. price, panel = self.calculate_panelprice() File "/home/ic/adk/dj/adkwoningdev/orders/models.py" in calculate_panelprice 1570. if len(panels) > 0: File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/query.py" in __len__ 122. self._fetch_all() File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/query.py" in _fetch_all 961. self._result_cache = list(self.iterator()) File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/query.py" in iterator 265. for row in compiler.results_iter(): File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in results_iter 694. for rows in self.execute_sql(MULTI): File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql 769. sql, params = self.as_sql() File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in as_sql 100. ordering, o_params, ordering_group_by = self.get_ordering() File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in get_ordering 424. self.query.get_meta(), default_order=asc): File "/home/ic/adk/dj/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in find_ordering_name 464. if field.rel and path and opts.ordering: Exception Type: AttributeError at /orders/price/ Exception Value: 'ManyToOneRel' object has no attribute 'rel'

Attachments (1)

test22466.diff (612 bytes ) - added by Tim Graham 10 years ago.

Download all attachments as: .zip

Change History (14)

comment:1 by Baptiste Mispelon, 10 years ago

Resolution: needsinfo
Status: newclosed

Hi,

Could you show your model definitions and the code you're using to trigger this error (if you could reduce this error to a short snippet of code, that would also really help in reproducing it)?

Thanks!

comment:2 by Takis Issaris, 10 years ago

Hi,

The codebase is large, so I will try to extract the smallest possible amount of code which still triggers the error. I cannot do it right away though, but I'll try to have a go after work this evening or tomorrow evening.

comment:3 by Takis Issaris, 10 years ago

Hi,

I finally found the time to minimize the application code, and track down the problem.

from django.db import models

class Article(models.Model):
    code = models.CharField(max_length=64, unique=True)
    description = models.CharField(max_length=256, blank=True)

class Price(models.Model):
    article = models.ForeignKey('Article', null=True)
    price = models.DecimalField(max_digits=8, decimal_places=2)
import logging
from django.http import HttpResponse
from orders.models import Article

def testbug(request):
    objs = Article.objects.all().order_by('price')
    logging.debug("len articles: %d", len(objs))
    return HttpResponse("")
from django.conf.urls import patterns, url
urlpatterns = patterns('',
    url(r'^orders/', 'orders.views.testbug'),
)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
SECRET_KEY = 'xyz'
DEBUG = True
TEMPLATE_DEBUG = True
ALLOWED_HOSTS = []
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'orders',
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'adkw.urls'
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
Environment:


Request Method: GET
Request URL: http://localhost:8001/orders/

Django Version: 1.7b3
Python Version: 2.7.6
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'orders')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Traceback:
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  113.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/ic/test/bug2/adkw/orders/views.py" in testbug
  7.     logging.debug("len articles: %d", len(objs))
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/query.py" in __len__
  122.         self._fetch_all()
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/query.py" in _fetch_all
  961.             self._result_cache = list(self.iterator())
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/query.py" in iterator
  265.         for row in compiler.results_iter():
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in results_iter
  698.         for rows in self.execute_sql(MULTI):
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  773.             sql, params = self.as_sql()
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in as_sql
  101.         ordering, o_params, ordering_group_by = self.get_ordering()
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in get_ordering
  428.                         self.query.get_meta(), default_order=asc):
File "/home/ic/test/bug2/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in find_ordering_name
  468.         if field.rel and path and opts.ordering:

Exception Type: AttributeError at /orders/
Exception Value: 'ManyToOneRel' object has no attribute 'rel'

In the view I'm ordering by the "price" field *which is not* in the Article class. For some reason this triggered no errors with Django 1.6 and earlier, only 1.7 complains.

Removing the 'orders' app from the INSTALLED_APPS list, gets rid of the error. I figured because it wasn't being used, but it appears Django 1.7 finds the apps anyway?

comment:4 by Takis Issaris, 10 years ago

Version: 1.7-beta-11.7-beta-2

comment:5 by Shai Berger, 10 years ago

I think you're getting an ordering on the reverse relation (that is, with the reduced app, ordering Article instances by the id's of related Price instances). You probably intended order_by('price__price') (if that works, and the original doesn't, this should probably be a release-blocker bug).

comment:6 by Takis Issaris, 10 years ago

I can confirm that replacing order_by('price') by order_by('price_ _price') as you stated, makes the exception disappear. I'm having a closer look at what's happening.

comment:7 by Takis Issaris, 10 years ago

I just compared the behaviors:

Django 1.6.4

objs = Article.objects.all()
SELECT "orders_article"."id", "orders_article"."code", "orders_article"."description"
FROM "orders_article" 
pk 1 code A price 2
pk 2 code B price 1

objs = Article.objects.all().order_by('price')
SELECT "orders_article"."id", "orders_article"."code", "orders_article"."description" 
FROM "orders_article"
LEFT OUTER JOIN "orders_price" ON ( "orders_article"."id" = "orders_price"."article_id" )
ORDER BY "orders_price"."id" ASC 
pk 1 code A price 2
pk 2 code B price 1

objs = Article.objects.all().order_by('price_ _price'):
SELECT "orders_article"."id", "orders_article"."code", "orders_article"."description"
FROM "orders_article"
LEFT OUTER JOIN "orders_price" ON ( "orders_article"."id" = "orders_price"."article_id" )
ORDER BY "orders_price"."price" ASC 
pk 2 code B price 1
pk 1 code A price 2

Django 1.7b3

objs = Article.objects.all()
SELECT "orders_article"."id", "orders_article"."code", "orders_article"."description"
FROM "orders_article" 
pk 1 code A price 2
pk 2 code B price 1

objs = Article.objects.all().order_by('price'):
'ManyToOneRel' object has no attribute 'rel'

objs = Article.objects.all().order_by('price_ _price'):
SELECT "orders_article"."id", "orders_article"."code", "orders_article"."description"
FROM "orders_article"
LEFT OUTER JOIN "orders_price" ON ( "orders_article"."id" = "orders_price"."article_id" )
ORDER BY "orders_price"."price" ASC 
pk 2 code B price 1
pk 1 code A price 2

comment:8 by Tim Graham, 10 years ago

Component: UncategorizedDatabase layer (models, ORM)
Resolution: needsinfo
Severity: NormalRelease blocker
Status: closednew
Triage Stage: UnreviewedAccepted

Bisected to 20bab2cf9d02a5c6477d8aac066a635986e0d3f3. Attaching a regression test.

by Tim Graham, 10 years ago

Attachment: test22466.diff added

comment:9 by Simon Charette, 10 years ago

Description: modified (diff)

I guess a simple getattr(field, 'rel', None) should fix this?

in reply to:  9 comment:10 by Shai Berger, 10 years ago

Replying to charettes:

I guess a simple getattr(field, 'rel', None) should fix this?

No, we need to fix it so that the field does have a rel attribute. This is exactly like #19195, from the other side of the relation.

comment:11 by loic84, 10 years ago

How does ordering by the reverse of a FK even work considering there are potentially many result?

comment:12 by Anssi Kääriäinen, 10 years ago

Owner: changed from nobody to Anssi Kääriäinen
Status: newassigned

Ordering by reverse FK is something that is possible even if it usually doesn't make any sense. The interpretation is that one gets duplicate results.

I'll assign this to myself.

comment:13 by Anssi Kääriäinen <akaariai@…>, 10 years ago

Resolution: fixed
Status: assignedclosed

In 76979a257d289b99998c463023308c888662d874:

[1.7.x] Fixed #22466 -- ordering by reverse foreign key

Ordering by reverse foreign key was broken by custom lookups patch
(commit 20bab2cf9d02a5c6477d8aac066a635986e0d3f3).

Thanks to everybody who helped solving this issue. Special thanks to
Trac alias takis for reporting this.

Backport of 3b7c66a3ac from master

Note: See TracTickets for help on using tickets.
Back to Top