[1.8.x] Fixed #24156 -- Fixed inherited related name of ManyToManyField.

Fixed situation when parent abstract model declares related_name='+'
and child models had an invalid queryset.

Backport of f7b2978158 from master
This commit is contained in:
Andriy Sokolovskiy 2015-05-09 13:57:13 +03:00 committed by Tim Graham
parent 15359f1fe9
commit eb85e6672a
6 changed files with 59 additions and 13 deletions

View File

@ -2540,7 +2540,7 @@ class ManyToManyField(RelatedField):
# related_name with one generated from the m2m field name. Django # related_name with one generated from the m2m field name. Django
# still uses backwards relations internally and we need to avoid # still uses backwards relations internally and we need to avoid
# clashes between multiple m2m fields with related_name == '+'. # clashes between multiple m2m fields with related_name == '+'.
self.rel.related_name = "_%s_+" % name self.rel.related_name = "_%s_%s_+" % (cls.__name__.lower(), name)
super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs) super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs)

View File

@ -17,3 +17,7 @@ Bugfixes
* Fixed system check crash on ``ForeignKey`` to abstract model * Fixed system check crash on ``ForeignKey`` to abstract model
(:ticket:`25503`). (:ticket:`25503`).
* Fixed incorrect queries when you have multiple ``ManyToManyField``\s on
different models that have the same field name, point to the same model, and
have their reverse relations disabled (:ticket:`25545`).

View File

@ -35,3 +35,20 @@ class Article(models.Model):
class Meta: class Meta:
ordering = ('headline',) ordering = ('headline',)
# Models to test correct related_name inheritance
class AbstractArticle(models.Model):
class Meta:
abstract = True
ordering = ('title',)
publications = models.ManyToManyField(Publication, name='publications', related_name='+')
class InheritedArticleA(AbstractArticle):
pass
class InheritedArticleB(AbstractArticle):
pass

View File

@ -4,7 +4,7 @@ from django.db import transaction
from django.test import TestCase from django.test import TestCase
from django.utils import six from django.utils import six
from .models import Article, Publication from .models import Article, InheritedArticleA, InheritedArticleB, Publication
class ManyToManyTests(TestCase): class ManyToManyTests(TestCase):
@ -415,3 +415,28 @@ class ManyToManyTests(TestCase):
self.assertQuerysetEqual(self.a4.publications.all(), []) self.assertQuerysetEqual(self.a4.publications.all(), [])
self.assertQuerysetEqual(self.p2.article_set.all(), self.assertQuerysetEqual(self.p2.article_set.all(),
['<Article: NASA finds intelligent life on Earth>']) ['<Article: NASA finds intelligent life on Earth>'])
def test_inherited_models_selects(self):
"""
#24156 - Objects from child models where the parent's m2m field uses
related_name='+' should be retrieved correctly.
"""
a = InheritedArticleA.objects.create()
b = InheritedArticleB.objects.create()
a.publications.add(self.p1, self.p2)
self.assertQuerysetEqual(a.publications.all(),
[
'<Publication: Science News>',
'<Publication: The Python Journal>',
])
self.assertQuerysetEqual(b.publications.all(), [])
b.publications.add(self.p3)
self.assertQuerysetEqual(a.publications.all(),
[
'<Publication: Science News>',
'<Publication: The Python Journal>',
])
self.assertQuerysetEqual(b.publications.all(),
[
'<Publication: Science Weekly>',
])

View File

@ -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),
('_people_hidden_+', None), ('_relating_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),
('_basepeople_hidden_+', None), ('_relating_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),
@ -382,8 +382,8 @@ TEST_RESULTS = {
Person: ( Person: (
('+', BasePerson), ('+', BasePerson),
('+', None), ('+', None),
('_basepeople_hidden_+', BasePerson), ('_relating_basepeople_hidden_+', BasePerson),
('_people_hidden_+', None), ('_relating_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),
('_basepeople_hidden_+', None), ('_relating_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),
('_basepeople_hidden_+', None), ('_relating_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),
('_basepeople_hidden_+', BasePerson), ('_relating_basepeople_hidden_+', BasePerson),
('friends_inherited_rel_+', None), ('friends_inherited_rel_+', None),
('followers_concrete', None), ('followers_concrete', None),
('relating_people', None), ('relating_people', None),
('_people_hidden_+', None), ('_relating_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_+', '_relating_basepeople_hidden_+',
], ],
Person: [ Person: [
'friends_inherited_rel_+', 'friends_inherited_rel_+',
'followers_concrete', 'followers_concrete',
'relating_people', 'relating_people',
'_people_hidden_+', '_relating_people_hidden_+',
], ],
Relation: [ Relation: [
'm2m_abstract_rel', 'm2m_abstract_rel',

View File

@ -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([
'+', '_basepeople_hidden_+', 'BasePerson_following_abstract+', 'BasePerson_following_abstract+', '+', '_relating_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+',