summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Fucci <marcofucci@gmail.com>2015-03-26 18:47:07 +0000
committerTim Graham <timograham@gmail.com>2015-09-30 18:38:49 -0400
commit0e2d3b93043676975aa921a70b5faafef02cac5c (patch)
tree86f5ba8e2a235b5016b5b6a60335063d2bbd3b7b
parent07e594bba22caa3f02023d0010ae5a6fb99773f0 (diff)
[1.8.x] 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 '+'. Backport of 4ee08958f154594b538207a53c1d457687b3f7ae from master
-rw-r--r--django/db/models/fields/related.py6
-rw-r--r--docs/releases/1.8.5.txt4
-rw-r--r--tests/m2m_regress/models.py11
-rw-r--r--tests/m2m_regress/tests.py13
-rw-r--r--tests/model_meta/results.py20
-rw-r--r--tests/model_meta/tests.py2
6 files changed, 44 insertions, 12 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index dec9e555f3..83e2a0aa8f 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -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)
diff --git a/docs/releases/1.8.5.txt b/docs/releases/1.8.5.txt
index 14893f4a72..b951ff98d8 100644
--- a/docs/releases/1.8.5.txt
+++ b/docs/releases/1.8.5.txt
@@ -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`).
diff --git a/tests/m2m_regress/models.py b/tests/m2m_regress/models.py
index 4c1538dcbf..57f02b8f90 100644
--- a/tests/m2m_regress/models.py
+++ b/tests/m2m_regress/models.py
@@ -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='+')
diff --git a/tests/m2m_regress/tests.py b/tests/m2m_regress/tests.py
index 885efa593f..5e2891b0b4 100644
--- a/tests/m2m_regress/tests.py
+++ b/tests/m2m_regress/tests.py
@@ -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>'])
diff --git a/tests/model_meta/results.py b/tests/model_meta/results.py
index d70963206f..858b70b73a 100644
--- a/tests/model_meta/results.py
+++ b/tests/model_meta/results.py
@@ -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',
diff --git a/tests/model_meta/tests.py b/tests/model_meta/tests.py
index c77a286308..6db9a0f710 100644
--- a/tests/model_meta/tests.py
+++ b/tests/model_meta/tests.py
@@ -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+',