Commit b018128e authored by Luke Plant's avatar Luke Plant
Browse files

Fixed #17838 - prefetch_related fails for GenericForeignKeys when related...

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
parent 8ef60df0
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -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__),
+9 −0
Original line number Diff line number Diff line
@@ -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


+9 −1
Original line number Diff line number Diff line
@@ -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
+7 −0
Original line number Diff line number Diff line
@@ -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]