From d7d7ad2881b66d2c6be45ab0b055f492d3b589e9 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Mon, 29 Oct 2012 13:40:32 +0000 Subject: [PATCH] [1.5.x] Fixed #17991 - prefetch_related fails with GenericRelation and varchar ID field Thanks to okke@formsma.nl for the report, and carmandrew@gmail.com for the tests. Backport of ccd14ff25b7642678bf3c9ed8a12643f04853144 from master --- django/contrib/contenttypes/generic.py | 6 ++++-- tests/modeltests/prefetch_related/models.py | 10 +++++++++- tests/modeltests/prefetch_related/tests.py | 10 ++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 29e93eefe7..726f4aa150 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals from collections import defaultdict from functools import partial -from operator import attrgetter from django.core.exceptions import ObjectDoesNotExist from django.db import connection @@ -329,8 +328,11 @@ def create_generic_related_manager(superclass): set(obj._get_pk_val() for obj in instances) } qs = super(GenericRelatedObjectManager, self).get_query_set().using(db).filter(**query) + # We (possibly) need to convert object IDs to the type of the + # instances' PK in order to match up instances: + object_id_converter = instances[0]._meta.pk.to_python return (qs, - attrgetter(self.object_id_field_name), + lambda relobj: object_id_converter(getattr(relobj, self.object_id_field_name)), lambda obj: obj._get_pk_val(), False, self.prefetch_cache_name) diff --git a/tests/modeltests/prefetch_related/models.py b/tests/modeltests/prefetch_related/models.py index 85488f0879..e58997d200 100644 --- a/tests/modeltests/prefetch_related/models.py +++ b/tests/modeltests/prefetch_related/models.py @@ -125,6 +125,10 @@ class TaggedItem(models.Model): related_name='taggeditem_set3') created_by_fkey = models.PositiveIntegerField(null=True) created_by = generic.GenericForeignKey('created_by_ct', 'created_by_fkey',) + favorite_ct = models.ForeignKey(ContentType, null=True, + related_name='taggeditem_set4') + favorite_fkey = models.CharField(max_length=64, null=True) + favorite = generic.GenericForeignKey('favorite_ct', 'favorite_fkey') def __str__(self): return self.tag @@ -132,7 +136,11 @@ class TaggedItem(models.Model): class Bookmark(models.Model): url = models.URLField() - tags = generic.GenericRelation(TaggedItem) + tags = generic.GenericRelation(TaggedItem, related_name='bookmarks') + favorite_tags = generic.GenericRelation(TaggedItem, + content_type_field='favorite_ct', + object_id_field='favorite_fkey', + related_name='favorite_bookmarks') class Comment(models.Model): diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py index 614a5fc1d6..e81560f01f 100644 --- a/tests/modeltests/prefetch_related/tests.py +++ b/tests/modeltests/prefetch_related/tests.py @@ -319,6 +319,16 @@ class GenericRelationTests(TestCase): for t in b.tags.all()] self.assertEqual(sorted(tags), ["django", "python"]) + def test_charfield_GFK(self): + b = Bookmark.objects.create(url='http://www.djangoproject.com/') + t1 = TaggedItem.objects.create(content_object=b, tag='django') + t2 = TaggedItem.objects.create(content_object=b, favorite=b, tag='python') + + with self.assertNumQueries(3): + bookmark = Bookmark.objects.filter(pk=b.pk).prefetch_related('tags', 'favorite_tags')[0] + self.assertEqual(sorted([i.tag for i in bookmark.tags.all()]), ["django", "python"]) + self.assertEqual([i.tag for i in bookmark.favorite_tags.all()], ["python"]) + class MultiTableInheritanceTest(TestCase):