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:
parent
8ef60df067
commit
b018128ea5
|
@ -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__),
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue