From 5ed205bc8a7940f3908b1db2cec32cfcabe47388 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick 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', , 1), (u'fatty', , 2), (u'salty', , 2), (u'shiny', , 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', , 1), (u'fatty', , 2), (u'salty', , 2), (u'shiny', , 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', , 1), (u'salty', , 2), (u'shiny', , 2)] +>>> ctype = ContentType.objects.get_for_model(lion) +>>> Animal.objects.filter(tags__content_type=ctype) +[] + """}