Opened 15 years ago

Closed 10 years ago

Last modified 10 years ago

#10913 closed Cleanup/optimization (fixed)

Document how related_name affects QuerySet filtering

Reported by: neithere Owned by: Tim Graham
Component: Documentation Version: dev
Severity: Normal Keywords: orm, related_name,
Cc: greg@… Triage Stage: Accepted
Has patch: no Needs documentation: yes
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The documentation states that by default the "backward" relationship Manager "is named FOO_set, where FOO is the source model name, lowercased". However, in the latest SVN snapshot the behaviour seems to be inconsistent.

models.py

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    title  = models.CharField(max_length=255)
    author = models.ForeignKey(Author)         # by default, related_name='book_set'

The related_name argument is commented out. If not, everything works fine, but let's see what happens now:

In shell after syncdb:

>>> from fooapp.models import Author, Book
>>> Author.book_set
<django.db.models.fields.related.ForeignRelatedObjectsDescriptor object at 0x27f4e90>
>>> Author.book
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: type object 'Author' has no attribute 'book'
>>> a=Author.objects.create(name='John')
>>> b=Book.objects.create(title='My Book', author=a)
>>> Author.objects.filter(book_set__exact=1)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/manager.py", line 129, in filter
    return self.get_query_set().filter(*args, **kwargs)
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/query.py", line 466, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/query.py", line 484, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/sql/query.py", line 1613, in add_q
    can_reuse=used_aliases)
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/sql/query.py", line 1511, in add_filter
    negate=negate, process_extras=process_extras)
  File "/usr/local/lib/python2.6/dist-packages/Django-1.1_beta_1-py2.6.egg/django/db/models/sql/query.py", line 1676, in setup_joins
    "Choices are: %s" % (name, ", ".join(names)))
FieldError: Cannot resolve keyword 'book_set' into field. Choices are: book, id, name
>>> Author.objects.filter(book__exact=1)
[<Author: John>]

Change History (11)

comment:1 by Karen Tracey, 15 years ago

Resolution: invalid
Status: newclosed

Everything you show looks correct. book_set is not a field, it is a type of queryset Manager that you can use to access the set of books by a particular Author instance. Therefore, since it is not a field, you cannot filter on it. Rather, you filter on the actual field you are interested in. If you have additional questions, please post to django-users, not the ticket tracker. What you have posted here indicates a misunderstanding about Django, how it works, and how to use it, not a bug in Django. The django-users mailing list will be a better place to get misunderstandings cleared up than the ticket tracker.

comment:2 by Greg Brown <greg@…>, 13 years ago

Cc: greg@… added
Easy pickings: unset
Resolution: invalid
Severity: Normal
Status: closedreopened
Type: Uncategorized
UI/UX: unset

I think the OP is correct here, at least in that the ORM is inconsistent. The docs state that the default related name is FOO_set, and while this is true when accessing the manager, the OP gets differing behaviour when explicitly setting related_name='book_set' to the default when filtering (as do I). To spell it out again, the following will work:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event, related_name='image_set')

>>> Event.objects.filter(image_set__isnull=True)

Whereas this will throw a FieldError:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event)

>>> Event.objects.filter(image_set__isnull=True)

This ticket does not indicate "a misunderstanding about Django, how it works, and how to use it"; rather it points out a gotcha that needs to be fixed, or at the very least properly documented.

comment:2 by Greg Brown <greg@…>, 13 years ago

Cc: greg@… added
Easy pickings: unset
Resolution: invalid
Severity: Normal
Status: closedreopened
Type: Uncategorized
UI/UX: unset

I think the OP is correct here, at least in that the ORM is inconsistent. The docs state that the default related name is FOO_set, and while this is true when accessing the manager, the OP gets differing behaviour when explicitly setting related_name='book_set' to the default when filtering (as do I). To spell it out again, the following will work:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event, related_name='image_set')

>>> Event.objects.filter(image_set__isnull=True)

Whereas this will throw a FieldError:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event)

>>> Event.objects.filter(image_set__isnull=True)

This ticket does not indicate "a misunderstanding about Django, how it works, and how to use it"; rather it points out a gotcha that needs to be fixed, or at the very least properly documented.

comment:2 by Greg Brown <greg@…>, 13 years ago

Easy pickings: unset
Resolution: invalid
Severity: Normal
Status: closedreopened
Type: Uncategorized
UI/UX: unset

I think the OP is correct here, at least in that the ORM is inconsistent. The docs state that the default related name is FOO_set, and while this is true when accessing the manager, the OP gets differing behaviour when explicitly setting related_name='book_set' to the default when filtering (as do I). To spell it out again, the following will work:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event, related_name='image_set')

>>> Event.objects.filter(image_set__isnull=True)

Whereas this will throw a FieldError:

class Event(models.Model):
    ...

class Image(models.Model):
    event = models.ForeignKey(Event)

>>> Event.objects.filter(image_set__isnull=True)

This ticket does not indicate "a misunderstanding about Django, how it works, and how to use it"; rather it points out a gotcha that needs to be fixed, or at the very least properly documented.

comment:3 by Greg Brown, 13 years ago

Sorry about that, I was getting gateway timeouts so hit refresh a couple of times.

comment:4 by Russell Keith-Magee, 13 years ago

Component: Database layer (models, ORM)Documentation
Needs documentation: set
Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization

The code example you provide is working as intended. However, I'll accept this ticket as a documentation issue; a quick scan of the related_name docs didn't reveal anything describing this behavior.

comment:5 by Greg Brown, 13 years ago

Fair enough, thanks for the quick response.

comment:6 by Aymeric Augustin, 11 years ago

Status: reopenednew

comment:7 by Tim Graham, 10 years ago

Owner: changed from nobody to Tim Graham
Status: newassigned
Summary: Default related_name is brokenDocument how related_name affects QuerySet filtering

comment:8 by Tim Graham <timograham@…>, 10 years ago

Resolution: fixed
Status: assignedclosed

In 75bb6ba96660e2a06e18d99120c05db2bb9fa9cc:

Fixed #10913 -- Documented how related_name affects QuerySet filtering

Thanks neithere for the suggestion.

comment:9 by Tim Graham <timograham@…>, 10 years ago

In 06b149e220f98cd643a4445db1b5719daac0d56f:

[1.6.x] Fixed #10913 -- Documented how related_name affects QuerySet filtering

Thanks neithere for the suggestion.

Backport of 75bb6ba966 from master

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