Loading django/db/models/fields/related.py +6 −0 Original line number Diff line number Diff line Loading @@ -2532,6 +2532,12 @@ class ManyToManyField(RelatedField): # clash. if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name): self.rel.related_name = "%s_rel_+" % name elif self.rel.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. self.rel.related_name = "_%s_+" % name super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs) Loading docs/releases/1.8.5.txt +4 −0 Original line number Diff line number Diff line Loading @@ -52,3 +52,7 @@ Bugfixes (:ticket:`24921`). * Fixed the ``manage.py test --keepdb`` option on Oracle (:ticket:`25421`). * Fixed incorrect queries with multiple many-to-many fields on a model with the same 'to' model and with ``related_name`` set to '+' (:ticket:`24505`, :ticket:`25486`). tests/m2m_regress/models.py +11 −0 Original line number Diff line number Diff line Loading @@ -54,9 +54,13 @@ class SelfReferChildSibling(SelfRefer): # Many-to-Many relation between models, where one of the PK's isn't an Autofield @python_2_unicode_compatible class Line(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Worksheet(models.Model): id = models.CharField(primary_key=True, max_length=100) Loading Loading @@ -87,3 +91,10 @@ class RegressionModelSplit(BadModelWithSplit): Model with a split method should not cause an error in add_lazy_relation """ others = models.ManyToManyField('self') # Regression for #24505 -- Two ManyToManyFields with the same "to" model # and related_name set to '+'. class Post(models.Model): primary_lines = models.ManyToManyField(Line, related_name='+') secondary_lines = models.ManyToManyField(Line, related_name='+') tests/m2m_regress/tests.py +12 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ from django.test import TestCase from django.utils import six from .models import ( Entry, RegressionModelSplit, SelfRefer, SelfReferChild, Entry, Line, Post, RegressionModelSplit, SelfRefer, SelfReferChild, SelfReferChildSibling, Tag, TagCollection, Worksheet, ) Loading Loading @@ -111,3 +111,14 @@ class M2MRegressionTests(TestCase): c1.refresh_from_db() self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"]) def test_multiple_forwards_only_m2m(self): # Regression for #24505 - Multiple ManyToManyFields to same "to" # model with related_name set to '+'. foo = Line.objects.create(name='foo') bar = Line.objects.create(name='bar') post = Post.objects.create() post.primary_lines.add(foo) post.secondary_lines.add(bar) self.assertQuerysetEqual(post.primary_lines.all(), ['<Line: foo>']) self.assertQuerysetEqual(post.secondary_lines.all(), ['<Line: bar>']) tests/model_meta/results.py +10 −10 Original line number Diff line number Diff line Loading @@ -319,7 +319,7 @@ TEST_RESULTS = { 'get_all_related_objects_with_model_hidden_local': { Person: ( ('+', None), ('+', None), ('_people_hidden_+', None), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), Loading @@ -334,7 +334,7 @@ TEST_RESULTS = { ), BasePerson: ( ('+', None), ('+', None), ('_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), Loading Loading @@ -381,9 +381,9 @@ TEST_RESULTS = { 'get_all_related_objects_with_model_hidden': { Person: ( ('+', BasePerson), ('+', BasePerson), ('+', None), ('+', None), ('_basepeople_hidden_+', BasePerson), ('_people_hidden_+', None), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_base+', BasePerson), Loading Loading @@ -416,7 +416,7 @@ TEST_RESULTS = { ), BasePerson: ( ('+', None), ('+', None), ('_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), Loading Loading @@ -730,7 +730,7 @@ TEST_RESULTS = { ('friends_base_rel_+', None), ('followers_base', None), ('relating_basepeople', None), ('+', None), ('_basepeople_hidden_+', None), ), Person: ( ('friends_abstract_rel_+', BasePerson), Loading @@ -738,11 +738,11 @@ TEST_RESULTS = { ('friends_base_rel_+', BasePerson), ('followers_base', BasePerson), ('relating_basepeople', BasePerson), ('+', BasePerson), ('_basepeople_hidden_+', BasePerson), ('friends_inherited_rel_+', None), ('followers_concrete', None), ('relating_people', None), ('+', None), ('_people_hidden_+', None), ), Relation: ( ('m2m_abstract_rel', None), Loading @@ -757,13 +757,13 @@ TEST_RESULTS = { 'friends_base_rel_+', 'followers_base', 'relating_basepeople', '+', '_basepeople_hidden_+', ], Person: [ 'friends_inherited_rel_+', 'followers_concrete', 'relating_people', '+', '_people_hidden_+', ], Relation: [ 'm2m_abstract_rel', Loading Loading
django/db/models/fields/related.py +6 −0 Original line number Diff line number Diff line Loading @@ -2532,6 +2532,12 @@ class ManyToManyField(RelatedField): # clash. if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name): self.rel.related_name = "%s_rel_+" % name elif self.rel.is_hidden(): # If the backwards relation is disabled, replace the original # related_name with one generated from the m2m field name. Django # still uses backwards relations internally and we need to avoid # clashes between multiple m2m fields with related_name == '+'. self.rel.related_name = "_%s_+" % name super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs) Loading
docs/releases/1.8.5.txt +4 −0 Original line number Diff line number Diff line Loading @@ -52,3 +52,7 @@ Bugfixes (:ticket:`24921`). * Fixed the ``manage.py test --keepdb`` option on Oracle (:ticket:`25421`). * Fixed incorrect queries with multiple many-to-many fields on a model with the same 'to' model and with ``related_name`` set to '+' (:ticket:`24505`, :ticket:`25486`).
tests/m2m_regress/models.py +11 −0 Original line number Diff line number Diff line Loading @@ -54,9 +54,13 @@ class SelfReferChildSibling(SelfRefer): # Many-to-Many relation between models, where one of the PK's isn't an Autofield @python_2_unicode_compatible class Line(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Worksheet(models.Model): id = models.CharField(primary_key=True, max_length=100) Loading Loading @@ -87,3 +91,10 @@ class RegressionModelSplit(BadModelWithSplit): Model with a split method should not cause an error in add_lazy_relation """ others = models.ManyToManyField('self') # Regression for #24505 -- Two ManyToManyFields with the same "to" model # and related_name set to '+'. class Post(models.Model): primary_lines = models.ManyToManyField(Line, related_name='+') secondary_lines = models.ManyToManyField(Line, related_name='+')
tests/m2m_regress/tests.py +12 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ from django.test import TestCase from django.utils import six from .models import ( Entry, RegressionModelSplit, SelfRefer, SelfReferChild, Entry, Line, Post, RegressionModelSplit, SelfRefer, SelfReferChild, SelfReferChildSibling, Tag, TagCollection, Worksheet, ) Loading Loading @@ -111,3 +111,14 @@ class M2MRegressionTests(TestCase): c1.refresh_from_db() self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"]) def test_multiple_forwards_only_m2m(self): # Regression for #24505 - Multiple ManyToManyFields to same "to" # model with related_name set to '+'. foo = Line.objects.create(name='foo') bar = Line.objects.create(name='bar') post = Post.objects.create() post.primary_lines.add(foo) post.secondary_lines.add(bar) self.assertQuerysetEqual(post.primary_lines.all(), ['<Line: foo>']) self.assertQuerysetEqual(post.secondary_lines.all(), ['<Line: bar>'])
tests/model_meta/results.py +10 −10 Original line number Diff line number Diff line Loading @@ -319,7 +319,7 @@ TEST_RESULTS = { 'get_all_related_objects_with_model_hidden_local': { Person: ( ('+', None), ('+', None), ('_people_hidden_+', None), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), Loading @@ -334,7 +334,7 @@ TEST_RESULTS = { ), BasePerson: ( ('+', None), ('+', None), ('_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), Loading Loading @@ -381,9 +381,9 @@ TEST_RESULTS = { 'get_all_related_objects_with_model_hidden': { Person: ( ('+', BasePerson), ('+', BasePerson), ('+', None), ('+', None), ('_basepeople_hidden_+', BasePerson), ('_people_hidden_+', None), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_base+', BasePerson), Loading Loading @@ -416,7 +416,7 @@ TEST_RESULTS = { ), BasePerson: ( ('+', None), ('+', None), ('_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), Loading Loading @@ -730,7 +730,7 @@ TEST_RESULTS = { ('friends_base_rel_+', None), ('followers_base', None), ('relating_basepeople', None), ('+', None), ('_basepeople_hidden_+', None), ), Person: ( ('friends_abstract_rel_+', BasePerson), Loading @@ -738,11 +738,11 @@ TEST_RESULTS = { ('friends_base_rel_+', BasePerson), ('followers_base', BasePerson), ('relating_basepeople', BasePerson), ('+', BasePerson), ('_basepeople_hidden_+', BasePerson), ('friends_inherited_rel_+', None), ('followers_concrete', None), ('relating_people', None), ('+', None), ('_people_hidden_+', None), ), Relation: ( ('m2m_abstract_rel', None), Loading @@ -757,13 +757,13 @@ TEST_RESULTS = { 'friends_base_rel_+', 'followers_base', 'relating_basepeople', '+', '_basepeople_hidden_+', ], Person: [ 'friends_inherited_rel_+', 'followers_concrete', 'relating_people', '+', '_people_hidden_+', ], Relation: [ 'm2m_abstract_rel', Loading