Hi, guys.
About a week ago i asked the same question
<http://stackoverflow.com/questions/28127135/is-django-prefetch-related-supposed-to-work-with-genericrelation>
on stackoverflow. Since i got no response there i started to read how to
report a bug
<https://docs.djangoproject.com/en/1.7/internals/contributing/bugs-and-features/>
(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.
--
You received this message because you are subscribed to the Google Groups
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/557ff1af-41d2-4676-90b9-cb19b2478730%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.