Fixed #17838 - prefetch_related fails for GenericForeignKeys when related object id is not a CharField/TextField

Thanks to mkai for the report and debugging, and tmitchell and Przemek
Lewandowski for their work on the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17744 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant 2012-03-15 15:06:57 +00:00
parent 8ef60df067
commit b018128ea5
4 changed files with 30 additions and 6 deletions

View File

@ -85,16 +85,16 @@ class GenericForeignKey(object):
ret_val.extend(ct.get_all_objects_for_this_type(pk__in=fkeys)) ret_val.extend(ct.get_all_objects_for_this_type(pk__in=fkeys))
# For doing the join in Python, we have to match both the FK val and the # For doing the join in Python, we have to match both the FK val and the
# content type, so the 'attr' vals we return need to be callables that # content type, so we use a callable that returns a (fk, class) pair.
# will return a (fk, class) pair.
def gfk_key(obj): def gfk_key(obj):
ct_id = getattr(obj, ct_attname) ct_id = getattr(obj, ct_attname)
if ct_id is None: if ct_id is None:
return None return None
else: else:
return (getattr(obj, self.fk_field), model = self.get_content_type(id=ct_id,
self.get_content_type(id=ct_id, using=obj._state.db).model_class()
using=obj._state.db).model_class()) return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)),
model)
return (ret_val, return (ret_val,
lambda obj: (obj._get_pk_val(), obj.__class__), lambda obj: (obj._get_pk_val(), obj.__class__),

View File

@ -125,6 +125,15 @@ class Bookmark(models.Model):
tags = generic.GenericRelation(TaggedItem) tags = generic.GenericRelation(TaggedItem)
class Comment(models.Model):
comment = models.TextField()
# Content-object field
content_type = models.ForeignKey(ContentType)
object_pk = models.TextField()
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
## Models for lookup ordering tests ## Models for lookup ordering tests

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from .models import (Author, Book, Reader, Qualification, Teacher, Department, from .models import (Author, Book, Reader, Qualification, Teacher, Department,
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge, TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
BookWithYear, Person, House, Room, Employee) BookWithYear, Person, House, Room, Employee, Comment)
class PrefetchRelatedTests(TestCase): class PrefetchRelatedTests(TestCase):
@ -254,6 +254,14 @@ class GenericRelationTests(TestCase):
qs = TaggedItem.objects.prefetch_related('content_object') qs = TaggedItem.objects.prefetch_related('content_object')
list(qs) list(qs)
def test_prefetch_GFK_nonint_pk(self):
Comment.objects.create(comment="awesome", content_object=self.book1)
# 1 for Comment table, 1 for Book table
with self.assertNumQueries(2):
qs = Comment.objects.prefetch_related('content_object')
[c.content_object for c in qs]
def test_traverse_GFK(self): def test_traverse_GFK(self):
""" """
Test that we can traverse a 'content_object' with prefetch_related() and Test that we can traverse a 'content_object' with prefetch_related() and

View File

@ -49,3 +49,10 @@ class CommentManagerTests(CommentTestCase):
author_comments = list(Comment.objects.for_model(Author.objects.get(pk=1))) author_comments = list(Comment.objects.for_model(Author.objects.get(pk=1)))
self.assertEqual(article_comments, [c1, c3]) self.assertEqual(article_comments, [c1, c3])
self.assertEqual(author_comments, [c2]) self.assertEqual(author_comments, [c2])
def testPrefetchRelated(self):
c1, c2, c3, c4 = self.createSomeComments()
# one for comments, one for Articles, one for Author
with self.assertNumQueries(3):
qs = Comment.objects.prefetch_related('content_object')
[c.content_object for c in qs]