Possible bug in prefetch_related used with Reverse generic relations

91 views
Skip to first unread message

Todor Velichkov

unread,
Feb 2, 2015, 3:12:22 AM2/2/15
to django...@googlegroups.com
Hi, guys.
About a week ago i asked the same question on stackoverflow. Since i got no response there i started to read how to report a bug (because I've never done it before), and this article took me here ;). I need more advanced opinion if this is truly a bug which should be reported or i just want too much from the ORM. So i will repeat myself from stackoverflow and explain what is all about. 

Here is some example model structure:

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType


class TaggedItem(models.Model):
 tag
= models.SlugField()
 content_type
= models.ForeignKey(ContentType)
 object_id
= models.PositiveIntegerField()
 content_object
= GenericForeignKey('content_type', 'object_id')

 
def __unicode__(self):
 
return self.tag

class Movie(models.Model):
 name
= models.CharField(max_length=100)

 
def __unicode__(self):
 
return self.name


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

 
def __unicode__(self):
 
return self.name


class Book(models.Model):
 name
= models.CharField(max_length=100)
 author
= models.ForeignKey(Author)
 tags
= GenericRelation(TaggedItem, related_query_name='books')

 
def __unicode__(self):
 
return self.name

Now, lets create some initial data:

>>> from tags.models import Book, Movie, Author, TaggedItem
>>>
a = Author(name='E L James')
>>> a.save()
>>> b1 = Book(name='Fifty Shades of Grey', author=a)
>>> b1.save()
>>> b2 = Book(name='Fifty Shades Darker', author=a)
>>>
b2.save()
>>> b3 = Book(name='Fifty Shades Freed', author=a)
>>>
b3.save()
>>>
t1 = TaggedItem(content_object=b1, tag='roman')
>>>
t1.save()
>>> t2 = TaggedItem(content_object=b2, tag='roman')
>>> t2.save()
>>> t3 = TaggedItem(content_object=b3, tag='roman')
>>>
t3.save()
>>>
m1 = Movie(name='Guardians of the Galaxy')
>>> m1.save()
>>>
t4 = TaggedItem(content_object=m1, tag='action movie')
>>> t4.save()


Now we can make a query like this:
>>> TaggedItem.objects.filter(books__author__name='E L James')
[<TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: roman>]


But we cant do this:

>>> TaggedItem.objects.filter(books__author__name='E L James').prefetch_related('books')
Traceback (most recent call last):
...
AttributeError: 'Book' object has no attribute 'object_id'


Why i think this may be a bug?
Because of the error. Normally if prefech_related can't resolve a relation it complains like that:
AttributeError: Cannot find 'some_field' on TaggedItem object, 'some_field' is an invalid parameter to prefetch_related()
But in the example above, Django actually tries to resolve the relation and fails because the ORM is searching for object_id inside the Book model instead of the TaggedItem model. 

Why i don't use the forward relation (the `content_type` field).
Yes, this actually will work:
>>> TaggedItem.objects.filter(books__author__name='E L James').prefetch_related('content_object')
[<TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: roman>]

 
But this way u can't go further with the relations against a queryset containing different types of content types.

>>> TaggedItem.objects.all().prefetch_related('content_object__author')
Traceback (most recent call last):
  ...
AttributeError: 'Movie' object has no attribute 'author_id'

 While the reversed API kind of a give me hope that it's the right way to go. I mean the ORM don't need to guess the author of which content types need  to be prefetched.
TaggedItem.objects.all().prefetch_related('books__author')
We clearly show that we want the authors of all book models. nothing more, nothing less.
 

Reply all
Reply to author
Forward
0 new messages