From 4c4d08502cbef6e0ed85470b2a5aade7fafa099f Mon Sep 17 00:00:00 2001
From: Luke Plant <L.Plant.98@cantab.net>
Date: Mon, 29 Oct 2012 13:40:32 +0000
Subject: [PATCH] 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.
---
 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):