Fixed #24505 -- Fixed clash with hidden m2m fields.
Added support for multiple m2m fields with the same 'to' model and with related_name set to '+'.
This commit is contained in:
parent
14f28f8233
commit
4ee08958f1
|
@ -2621,6 +2621,12 @@ class ManyToManyField(RelatedField):
|
||||||
if self.remote_field.symmetrical and (
|
if self.remote_field.symmetrical and (
|
||||||
self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name):
|
self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name):
|
||||||
self.remote_field.related_name = "%s_rel_+" % name
|
self.remote_field.related_name = "%s_rel_+" % name
|
||||||
|
elif self.remote_field.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.remote_field.related_name = "_%s_+" % name
|
||||||
|
|
||||||
super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs)
|
super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,13 @@ class SelfReferChildSibling(SelfRefer):
|
||||||
|
|
||||||
|
|
||||||
# Many-to-Many relation between models, where one of the PK's isn't an Autofield
|
# Many-to-Many relation between models, where one of the PK's isn't an Autofield
|
||||||
|
@python_2_unicode_compatible
|
||||||
class Line(models.Model):
|
class Line(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Worksheet(models.Model):
|
class Worksheet(models.Model):
|
||||||
id = models.CharField(primary_key=True, max_length=100)
|
id = models.CharField(primary_key=True, max_length=100)
|
||||||
|
@ -87,3 +91,10 @@ class RegressionModelSplit(BadModelWithSplit):
|
||||||
Model with a split method should not cause an error in add_lazy_relation
|
Model with a split method should not cause an error in add_lazy_relation
|
||||||
"""
|
"""
|
||||||
others = models.ManyToManyField('self')
|
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='+')
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.test import TestCase
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Entry, RegressionModelSplit, SelfRefer, SelfReferChild,
|
Entry, Line, Post, RegressionModelSplit, SelfRefer, SelfReferChild,
|
||||||
SelfReferChildSibling, Tag, TagCollection, Worksheet,
|
SelfReferChildSibling, Tag, TagCollection, Worksheet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -111,3 +111,14 @@ class M2MRegressionTests(TestCase):
|
||||||
|
|
||||||
c1.refresh_from_db()
|
c1.refresh_from_db()
|
||||||
self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"])
|
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>'])
|
||||||
|
|
|
@ -319,7 +319,7 @@ TEST_RESULTS = {
|
||||||
'get_all_related_objects_with_model_hidden_local': {
|
'get_all_related_objects_with_model_hidden_local': {
|
||||||
Person: (
|
Person: (
|
||||||
('+', None),
|
('+', None),
|
||||||
('+', None),
|
('_people_hidden_+', None),
|
||||||
('Person_following_inherited+', None),
|
('Person_following_inherited+', None),
|
||||||
('Person_following_inherited+', None),
|
('Person_following_inherited+', None),
|
||||||
('Person_friends_inherited+', None),
|
('Person_friends_inherited+', None),
|
||||||
|
@ -334,7 +334,7 @@ TEST_RESULTS = {
|
||||||
),
|
),
|
||||||
BasePerson: (
|
BasePerson: (
|
||||||
('+', None),
|
('+', None),
|
||||||
('+', None),
|
('_basepeople_hidden_+', None),
|
||||||
('BasePerson_following_abstract+', None),
|
('BasePerson_following_abstract+', None),
|
||||||
('BasePerson_following_abstract+', None),
|
('BasePerson_following_abstract+', None),
|
||||||
('BasePerson_following_base+', None),
|
('BasePerson_following_base+', None),
|
||||||
|
@ -380,10 +380,10 @@ TEST_RESULTS = {
|
||||||
},
|
},
|
||||||
'get_all_related_objects_with_model_hidden': {
|
'get_all_related_objects_with_model_hidden': {
|
||||||
Person: (
|
Person: (
|
||||||
('+', BasePerson),
|
|
||||||
('+', BasePerson),
|
('+', BasePerson),
|
||||||
('+', None),
|
('+', None),
|
||||||
('+', None),
|
('_basepeople_hidden_+', BasePerson),
|
||||||
|
('_people_hidden_+', None),
|
||||||
('BasePerson_following_abstract+', BasePerson),
|
('BasePerson_following_abstract+', BasePerson),
|
||||||
('BasePerson_following_abstract+', BasePerson),
|
('BasePerson_following_abstract+', BasePerson),
|
||||||
('BasePerson_following_base+', BasePerson),
|
('BasePerson_following_base+', BasePerson),
|
||||||
|
@ -416,7 +416,7 @@ TEST_RESULTS = {
|
||||||
),
|
),
|
||||||
BasePerson: (
|
BasePerson: (
|
||||||
('+', None),
|
('+', None),
|
||||||
('+', None),
|
('_basepeople_hidden_+', None),
|
||||||
('BasePerson_following_abstract+', None),
|
('BasePerson_following_abstract+', None),
|
||||||
('BasePerson_following_abstract+', None),
|
('BasePerson_following_abstract+', None),
|
||||||
('BasePerson_following_base+', None),
|
('BasePerson_following_base+', None),
|
||||||
|
@ -730,7 +730,7 @@ TEST_RESULTS = {
|
||||||
('friends_base_rel_+', None),
|
('friends_base_rel_+', None),
|
||||||
('followers_base', None),
|
('followers_base', None),
|
||||||
('relating_basepeople', None),
|
('relating_basepeople', None),
|
||||||
('+', None),
|
('_basepeople_hidden_+', None),
|
||||||
),
|
),
|
||||||
Person: (
|
Person: (
|
||||||
('friends_abstract_rel_+', BasePerson),
|
('friends_abstract_rel_+', BasePerson),
|
||||||
|
@ -738,11 +738,11 @@ TEST_RESULTS = {
|
||||||
('friends_base_rel_+', BasePerson),
|
('friends_base_rel_+', BasePerson),
|
||||||
('followers_base', BasePerson),
|
('followers_base', BasePerson),
|
||||||
('relating_basepeople', BasePerson),
|
('relating_basepeople', BasePerson),
|
||||||
('+', BasePerson),
|
('_basepeople_hidden_+', BasePerson),
|
||||||
('friends_inherited_rel_+', None),
|
('friends_inherited_rel_+', None),
|
||||||
('followers_concrete', None),
|
('followers_concrete', None),
|
||||||
('relating_people', None),
|
('relating_people', None),
|
||||||
('+', None),
|
('_people_hidden_+', None),
|
||||||
),
|
),
|
||||||
Relation: (
|
Relation: (
|
||||||
('m2m_abstract_rel', None),
|
('m2m_abstract_rel', None),
|
||||||
|
@ -757,13 +757,13 @@ TEST_RESULTS = {
|
||||||
'friends_base_rel_+',
|
'friends_base_rel_+',
|
||||||
'followers_base',
|
'followers_base',
|
||||||
'relating_basepeople',
|
'relating_basepeople',
|
||||||
'+',
|
'_basepeople_hidden_+',
|
||||||
],
|
],
|
||||||
Person: [
|
Person: [
|
||||||
'friends_inherited_rel_+',
|
'friends_inherited_rel_+',
|
||||||
'followers_concrete',
|
'followers_concrete',
|
||||||
'relating_people',
|
'relating_people',
|
||||||
'+',
|
'_people_hidden_+',
|
||||||
],
|
],
|
||||||
Relation: [
|
Relation: [
|
||||||
'm2m_abstract_rel',
|
'm2m_abstract_rel',
|
||||||
|
|
|
@ -237,7 +237,7 @@ class RelationTreeTests(TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted([field.related_query_name() for field in BasePerson._meta._relation_tree]),
|
sorted([field.related_query_name() for field in BasePerson._meta._relation_tree]),
|
||||||
sorted([
|
sorted([
|
||||||
'+', '+', 'BasePerson_following_abstract+', 'BasePerson_following_abstract+',
|
'+', '_basepeople_hidden_+', 'BasePerson_following_abstract+', 'BasePerson_following_abstract+',
|
||||||
'BasePerson_following_base+', 'BasePerson_following_base+', 'BasePerson_friends_abstract+',
|
'BasePerson_following_base+', 'BasePerson_following_base+', 'BasePerson_friends_abstract+',
|
||||||
'BasePerson_friends_abstract+', 'BasePerson_friends_base+', 'BasePerson_friends_base+',
|
'BasePerson_friends_abstract+', 'BasePerson_friends_base+', 'BasePerson_friends_base+',
|
||||||
'BasePerson_m2m_abstract+', 'BasePerson_m2m_base+', 'Relating_basepeople+',
|
'BasePerson_m2m_abstract+', 'BasePerson_m2m_base+', 'Relating_basepeople+',
|
||||||
|
|
Loading…
Reference in New Issue