Commit 4ee08958 authored by Marco Fucci's avatar Marco Fucci Committed by Tim Graham
Browse files

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 '+'.
parent 14f28f82
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -2621,6 +2621,12 @@ class ManyToManyField(RelatedField):
        if self.remote_field.symmetrical and (
                self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_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)

+11 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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='+')
+12 −1
Original line number Diff line number Diff line
@@ -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,
)

@@ -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>'])
+10 −10
Original line number Diff line number Diff line
@@ -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),
@@ -334,7 +334,7 @@ TEST_RESULTS = {
        ),
        BasePerson: (
            ('+', None),
            ('+', None),
            ('_basepeople_hidden_+', None),
            ('BasePerson_following_abstract+', None),
            ('BasePerson_following_abstract+', None),
            ('BasePerson_following_base+', None),
@@ -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),
@@ -416,7 +416,7 @@ TEST_RESULTS = {
        ),
        BasePerson: (
            ('+', None),
            ('+', None),
            ('_basepeople_hidden_+', None),
            ('BasePerson_following_abstract+', None),
            ('BasePerson_following_abstract+', None),
            ('BasePerson_following_base+', None),
@@ -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),
@@ -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),
@@ -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',
+1 −1
Original line number Diff line number Diff line
@@ -237,7 +237,7 @@ class RelationTreeTests(TestCase):
        self.assertEqual(
            sorted([field.related_query_name() for field in BasePerson._meta._relation_tree]),
            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_friends_abstract+', 'BasePerson_friends_base+', 'BasePerson_friends_base+',
                'BasePerson_m2m_abstract+', 'BasePerson_m2m_base+', 'Relating_basepeople+',