diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index bd453e53b1..369674563a 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -716,7 +716,7 @@ class OneToOneField(ForeignKey): SingleRelatedObjectDescriptor(related)) if not cls._meta.one_to_one_field: cls._meta.one_to_one_field = self - + def formfield(self, **kwargs): if self.rel.parent_link: return None @@ -844,6 +844,15 @@ class ManyToManyField(RelatedField, Field): return smart_unicode(data) def contribute_to_class(self, cls, name): + # To support multiple relations to self, it's useful to have a non-None + # related name on symmetrical relations for internal reasons. The + # concept doesn't make a lot of sense externally ("you want me to + # specify *what* on my non-reversible relation?!"), so we set it up + # automatically. The funky name reduces the chance of an accidental + # clash. + if self.rel.symmetrical and self.rel.related_name is None: + self.rel.related_name = "%s_rel_+" % name + super(ManyToManyField, self).contribute_to_class(cls, name) # Add the descriptor for the m2m relation setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) diff --git a/tests/regressiontests/m2m_regress/__init__.py b/tests/regressiontests/m2m_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/m2m_regress/models.py b/tests/regressiontests/m2m_regress/models.py new file mode 100644 index 0000000000..e641d3dadb --- /dev/null +++ b/tests/regressiontests/m2m_regress/models.py @@ -0,0 +1,56 @@ +from django.db import models + +# No related name is needed here, since symmetrical relations are not +# explicitly reversible. +class SelfRefer(models.Model): + name = models.CharField(max_length=10) + references = models.ManyToManyField('self') + related = models.ManyToManyField('self') + + def __unicode__(self): + return self.name + +class Tag(models.Model): + name = models.CharField(max_length=10) + + def __unicode__(self): + return self.name + +# A related_name is required on one of the ManyToManyField entries here because +# they are both addressable as reverse relations from Tag. +class Entry(models.Model): + name = models.CharField(max_length=10) + topics = models.ManyToManyField(Tag) + related = models.ManyToManyField(Tag, related_name="similar") + + def __unicode__(self): + return self.name + +__test__ = {"regressions": """ +# Multiple m2m references to the same model or a different model must be +# distinguished when accessing the relations through an instance attribute. + +>>> s1 = SelfRefer.objects.create(name='s1') +>>> s2 = SelfRefer.objects.create(name='s2') +>>> s3 = SelfRefer.objects.create(name='s3') +>>> s1.references.add(s2) +>>> s1.related.add(s3) + +>>> e1 = Entry.objects.create(name='e1') +>>> t1 = Tag.objects.create(name='t1') +>>> t2 = Tag.objects.create(name='t2') +>>> e1.topics.add(t1) +>>> e1.related.add(t2) + +>>> s1.references.all() +[] +>>> s1.related.all() +[] + +>>> e1.topics.all() +[] +>>> e1.related.all() +[] + +""" +}