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))
# 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
# will return a (fk, class) pair.
# content type, so we use a callable that returns a (fk, class) pair.
def gfk_key(obj):
ct_id = getattr(obj, ct_attname)
if ct_id is None:
return None
else:
return (getattr(obj, self.fk_field),
self.get_content_type(id=ct_id,
using=obj._state.db).model_class())
model = self.get_content_type(id=ct_id,
using=obj._state.db).model_class()
return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)),
model)
return (ret_val,
lambda obj: (obj._get_pk_val(), obj.__class__),

View File

@ -125,6 +125,15 @@ class Bookmark(models.Model):
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

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from .models import (Author, Book, Reader, Qualification, Teacher, Department,
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
BookWithYear, Person, House, Room, Employee)
BookWithYear, Person, House, Room, Employee, Comment)
class PrefetchRelatedTests(TestCase):
@ -254,6 +254,14 @@ class GenericRelationTests(TestCase):
qs = TaggedItem.objects.prefetch_related('content_object')
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):
"""
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)))
self.assertEqual(article_comments, [c1, c3])
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]