Fixed #24156 -- Fixed inherited related name of ManyToManyField.

Fixed situation when parent abstract model declares related_name='+',
and child models had an invalid queryset.
This commit is contained in:
Andriy Sokolovskiy 2015-05-09 13:57:13 +03:00 committed by Tim Graham
parent ec74dba2ab
commit f7b2978158
5 changed files with 55 additions and 13 deletions

View File

@ -2626,7 +2626,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.remote_field.related_name = "_%s_+" % name self.remote_field.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

@ -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):
@ -454,3 +454,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

@ -248,7 +248,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+',