From 5ed205bc8a7940f3908b1db2cec32cfcabe47388 Mon Sep 17 00:00:00 2001
From: Malcolm Tredinnick <malcolm.tredinnick@gmail.com>
Date: Sun, 9 Dec 2007 07:12:07 +0000
Subject: [PATCH] Fixed #3906 -- Fixed the reverse_m2m_name for a generic
 relation. Refs #2749.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6900 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
 django/contrib/contenttypes/generic.py       | 42 ++++++++++----------
 tests/modeltests/generic_relations/models.py | 30 ++++++++------
 2 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index b738a269ec..e496ed1af8 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -16,18 +16,18 @@ class GenericForeignKey(object):
     Provides a generic relation to any object through content-type/object-id
     fields.
     """
-    
+
     def __init__(self, ct_field="content_type", fk_field="object_id"):
         self.ct_field = ct_field
         self.fk_field = fk_field
-        
+
     def contribute_to_class(self, cls, name):
-        # Make sure the fields exist (these raise FieldDoesNotExist, 
+        # Make sure the fields exist (these raise FieldDoesNotExist,
         # which is a fine error to raise here)
         self.name = name
         self.model = cls
         self.cache_attr = "_%s_cache" % name
-        
+
         # For some reason I don't totally understand, using weakrefs here doesn't work.
         dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
 
@@ -35,18 +35,18 @@ class GenericForeignKey(object):
         setattr(cls, name, self)
 
     def instance_pre_init(self, signal, sender, args, kwargs):
-        # Handle initalizing an object with the generic FK instaed of 
-        # content-type/object-id fields.        
+        # Handle initalizing an object with the generic FK instaed of
+        # content-type/object-id fields.
         if self.name in kwargs:
             value = kwargs.pop(self.name)
             kwargs[self.ct_field] = self.get_content_type(value)
             kwargs[self.fk_field] = value._get_pk_val()
-            
+
     def get_content_type(self, obj):
         # Convenience function using get_model avoids a circular import when using this model
         ContentType = get_model("contenttypes", "contenttype")
         return ContentType.objects.get_for_model(obj)
-        
+
     def __get__(self, instance, instance_type=None):
         if instance is None:
             raise AttributeError, u"%s must be accessed via instance" % self.name
@@ -77,21 +77,21 @@ class GenericForeignKey(object):
         setattr(instance, self.ct_field, ct)
         setattr(instance, self.fk_field, fk)
         setattr(instance, self.cache_attr, value)
-    
+
 class GenericRelation(RelatedField, Field):
     """Provides an accessor to generic related objects (i.e. comments)"""
 
     def __init__(self, to, **kwargs):
         kwargs['verbose_name'] = kwargs.get('verbose_name', None)
-        kwargs['rel'] = GenericRel(to, 
+        kwargs['rel'] = GenericRel(to,
                             related_name=kwargs.pop('related_name', None),
                             limit_choices_to=kwargs.pop('limit_choices_to', None),
                             symmetrical=kwargs.pop('symmetrical', True))
-                            
+
         # Override content-type/object-id field names on the related class
         self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
-        self.content_type_field_name = kwargs.pop("content_type_field", "content_type")                
-        
+        self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
+
         kwargs['blank'] = True
         kwargs['editable'] = False
         kwargs['serialize'] = False
@@ -116,9 +116,9 @@ class GenericRelation(RelatedField, Field):
 
     def m2m_column_name(self):
         return self.object_id_field_name
-        
+
     def m2m_reverse_name(self):
-        return self.object_id_field_name
+        return self.model._meta.pk.column
 
     def contribute_to_class(self, cls, name):
         super(GenericRelation, self).contribute_to_class(cls, name)
@@ -131,13 +131,13 @@ class GenericRelation(RelatedField, Field):
 
     def contribute_to_related_class(self, cls, related):
         pass
-        
+
     def set_attributes_from_rel(self):
         pass
 
     def get_internal_type(self):
         return "ManyToManyField"
-        
+
 class ReverseGenericRelatedObjectsDescriptor(object):
     """
     This class provides the functionality that makes the related-object
@@ -193,12 +193,12 @@ def create_generic_related_manager(superclass):
     Factory function for a manager that subclasses 'superclass' (which is a
     Manager) and adds behavior for generic related objects.
     """
-    
+
     class GenericRelatedObjectManager(superclass):
         def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
                      join_table=None, source_col_name=None, target_col_name=None, content_type=None,
                      content_type_field_name=None, object_id_field_name=None):
-            
+
             super(GenericRelatedObjectManager, self).__init__()
             self.core_filters = core_filters or {}
             self.model = model
@@ -212,10 +212,10 @@ def create_generic_related_manager(superclass):
             self.content_type_field_name = content_type_field_name
             self.object_id_field_name = object_id_field_name
             self.pk_val = self.instance._get_pk_val()
-                        
+
         def get_query_set(self):
             query = {
-                '%s__pk' % self.content_type_field_name : self.content_type.id, 
+                '%s__pk' % self.content_type_field_name : self.content_type.id,
                 '%s__exact' % self.object_id_field_name : self.pk_val,
             }
             return superclass.get_query_set(self).filter(**query)
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index ce1d824ca8..ff86823d07 100644
--- a/tests/modeltests/generic_relations/models.py
+++ b/tests/modeltests/generic_relations/models.py
@@ -18,42 +18,42 @@ class TaggedItem(models.Model):
     tag = models.SlugField()
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    
+
     content_object = generic.GenericForeignKey()
-    
+
     class Meta:
         ordering = ["tag"]
-    
+
     def __unicode__(self):
         return self.tag
 
 class Animal(models.Model):
     common_name = models.CharField(max_length=150)
     latin_name = models.CharField(max_length=150)
-    
+
     tags = generic.GenericRelation(TaggedItem)
 
     def __unicode__(self):
         return self.common_name
-        
+
 class Vegetable(models.Model):
     name = models.CharField(max_length=150)
     is_yucky = models.BooleanField(default=True)
-    
+
     tags = generic.GenericRelation(TaggedItem)
-    
+
     def __unicode__(self):
         return self.name
-    
+
 class Mineral(models.Model):
     name = models.CharField(max_length=150)
     hardness = models.PositiveSmallIntegerField()
-    
+
     # note the lack of an explicit GenericRelation here...
-    
+
     def __unicode__(self):
         return self.name
-        
+
 __test__ = {'API_TESTS':"""
 # Create the world in 7 lines of code...
 >>> lion = Animal(common_name="Lion", latin_name="Panthera leo")
@@ -117,13 +117,13 @@ __test__ = {'API_TESTS':"""
 >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
 [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
 
-# If Generic Relation is not explicitly defined, any related objects 
+# If Generic Relation is not explicitly defined, any related objects
 # remain after deletion of the source object.
 >>> quartz.delete()
 >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
 [(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
 
-# If you delete a tag, the objects using the tag are unaffected 
+# If you delete a tag, the objects using the tag are unaffected
 # (other than losing a tag)
 >>> tag = TaggedItem.objects.get(id=1)
 >>> tag.delete()
@@ -132,4 +132,8 @@ __test__ = {'API_TESTS':"""
 >>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
 [(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
 
+>>> ctype = ContentType.objects.get_for_model(lion)
+>>> Animal.objects.filter(tags__content_type=ctype)
+[<Animal: Platypus>]
+
 """}