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.