Refs #24215 -- Prevented pending lookup pollution by abstract models.

This commit is contained in:
Simon Charette 2015-02-12 01:28:24 -05:00
parent e519ee1d35
commit 9239f1dda7
3 changed files with 66 additions and 20 deletions

View File

@ -291,20 +291,21 @@ class RelatedField(Field):
if hasattr(sup, 'contribute_to_class'): if hasattr(sup, 'contribute_to_class'):
sup.contribute_to_class(cls, name, virtual_only=virtual_only) sup.contribute_to_class(cls, name, virtual_only=virtual_only)
if not cls._meta.abstract and self.rel.related_name: if not cls._meta.abstract:
related_name = force_text(self.rel.related_name) % { if self.rel.related_name:
'class': cls.__name__.lower(), related_name = force_text(self.rel.related_name) % {
'app_label': cls._meta.app_label.lower() 'class': cls.__name__.lower(),
} 'app_label': cls._meta.app_label.lower()
self.rel.related_name = related_name }
other = self.rel.to self.rel.related_name = related_name
if isinstance(other, six.string_types) or other._meta.pk is None: other = self.rel.to
def resolve_related_class(field, model, cls): if isinstance(other, six.string_types) or other._meta.pk is None:
field.rel.to = model def resolve_related_class(field, model, cls):
field.do_related_class(model, cls) field.rel.to = model
add_lazy_relation(cls, self, other, resolve_related_class) field.do_related_class(model, cls)
else: add_lazy_relation(cls, self, other, resolve_related_class)
self.do_related_class(other, cls) else:
self.do_related_class(other, cls)
@property @property
def swappable_setting(self): def swappable_setting(self):
@ -2605,7 +2606,7 @@ class ManyToManyField(RelatedField):
# Populate some necessary rel arguments so that cross-app relations # Populate some necessary rel arguments so that cross-app relations
# work correctly. # work correctly.
if isinstance(self.rel.through, six.string_types): if not cls._meta.abstract and isinstance(self.rel.through, six.string_types):
def resolve_through_model(field, model, cls): def resolve_through_model(field, model, cls):
field.rel.through = model field.rel.through = model
add_lazy_relation(cls, self, self.rel.through, resolve_through_model) add_lazy_relation(cls, self, self.rel.through, resolve_through_model)

View File

@ -367,3 +367,14 @@ class NullableUUIDModel(models.Model):
class PrimaryKeyUUIDModel(models.Model): class PrimaryKeyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4) id = models.UUIDField(primary_key=True, default=uuid.uuid4)
###############################################################################
# See ticket #24215.
class AbstractForeignFieldsModel(models.Model):
fk = models.ForeignKey('missing.FK')
m2m = models.ManyToManyField('missing.M2M', through='missing.Through')
class Meta:
abstract = True

View File

@ -21,11 +21,12 @@ from django.utils import six
from django.utils.functional import lazy from django.utils.functional import lazy
from .models import ( from .models import (
Bar, BigD, BigIntegerModel, BigS, BooleanModel, DataModel, DateTimeModel, AbstractForeignFieldsModel, Bar, BigD, BigIntegerModel, BigS, BooleanModel,
Document, FksToBooleans, FkToChar, FloatModel, Foo, GenericIPAddress, DataModel, DateTimeModel, Document, FksToBooleans, FkToChar, FloatModel,
IntegerModel, NullBooleanModel, PositiveIntegerModel, Foo, GenericIPAddress, IntegerModel, NullBooleanModel,
PositiveSmallIntegerModel, Post, PrimaryKeyCharModel, RenamedField, PositiveIntegerModel, PositiveSmallIntegerModel, Post, PrimaryKeyCharModel,
SmallIntegerModel, VerboseNameField, Whiz, WhizIter, WhizIterEmpty, RenamedField, SmallIntegerModel, VerboseNameField, Whiz, WhizIter,
WhizIterEmpty,
) )
@ -201,6 +202,39 @@ class ForeignKeyTests(test.TestCase):
rel_name = Bar._meta.get_field('a').rel.related_name rel_name = Bar._meta.get_field('a').rel.related_name
self.assertIsInstance(rel_name, six.text_type) self.assertIsInstance(rel_name, six.text_type)
def test_abstract_model_pending_lookups(self):
"""
Foreign key fields declared on abstract models should not add lazy relations to
resolve relationship declared as string. refs #24215
"""
opts = AbstractForeignFieldsModel._meta
to_key = ('missing', 'FK')
fk_lookup = (AbstractForeignFieldsModel, opts.get_field('fk'))
self.assertFalse(
any(lookup[0:2] == fk_lookup for lookup in opts.apps._pending_lookups.get(to_key, [])),
'Pending lookup added for the abstract model foreign key `to` parameter'
)
class ManyToManyFieldTests(test.TestCase):
def test_abstract_model_pending_lookups(self):
"""
Many-to-many fields declared on abstract models should not add lazy relations to
resolve relationship declared as string. refs #24215
"""
opts = AbstractForeignFieldsModel._meta
to_key = ('missing', 'M2M')
fk_lookup = (AbstractForeignFieldsModel, opts.get_field('m2m'))
self.assertFalse(
any(lookup[0:2] == fk_lookup for lookup in opts.apps._pending_lookups.get(to_key, [])),
'Pending lookup added for the abstract model many-to-many `to` parameter.'
)
through_key = ('missing', 'Through')
self.assertFalse(
any(lookup[0:2] == fk_lookup for lookup in opts.apps._pending_lookups.get(through_key, [])),
'Pending lookup added for the abstract model many-to-many `through` parameter.'
)
class DateTimeFieldTests(unittest.TestCase): class DateTimeFieldTests(unittest.TestCase):
def test_datetimefield_to_python_usecs(self): def test_datetimefield_to_python_usecs(self):