Fixed #32676 -- Prevented migrations from rendering related field attributes when not passed during initialization.
Thanks Simon Charette for the implementation idea.
This commit is contained in:
parent
b746596f5f
commit
b9df2b74b9
|
@ -89,6 +89,18 @@ class RelatedField(FieldCacheMixin, Field):
|
||||||
many_to_many = False
|
many_to_many = False
|
||||||
many_to_one = False
|
many_to_one = False
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
related_name=None,
|
||||||
|
related_query_name=None,
|
||||||
|
limit_choices_to=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
self._related_name = related_name
|
||||||
|
self._related_query_name = related_query_name
|
||||||
|
self._limit_choices_to = limit_choices_to
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def related_model(self):
|
def related_model(self):
|
||||||
# Can't cache this property until all the models are loaded.
|
# Can't cache this property until all the models are loaded.
|
||||||
|
@ -319,12 +331,12 @@ class RelatedField(FieldCacheMixin, Field):
|
||||||
|
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
name, path, args, kwargs = super().deconstruct()
|
name, path, args, kwargs = super().deconstruct()
|
||||||
if self.remote_field.limit_choices_to:
|
if self._limit_choices_to:
|
||||||
kwargs['limit_choices_to'] = self.remote_field.limit_choices_to
|
kwargs['limit_choices_to'] = self._limit_choices_to
|
||||||
if self.remote_field.related_name is not None:
|
if self._related_name is not None:
|
||||||
kwargs['related_name'] = self.remote_field.related_name
|
kwargs['related_name'] = self._related_name
|
||||||
if self.remote_field.related_query_name is not None:
|
if self._related_query_name is not None:
|
||||||
kwargs['related_query_name'] = self.remote_field.related_query_name
|
kwargs['related_query_name'] = self._related_query_name
|
||||||
return name, path, args, kwargs
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_forward_related_filter(self, obj):
|
def get_forward_related_filter(self, obj):
|
||||||
|
@ -471,7 +483,13 @@ class ForeignObject(RelatedField):
|
||||||
on_delete=on_delete,
|
on_delete=on_delete,
|
||||||
)
|
)
|
||||||
|
|
||||||
super().__init__(rel=rel, **kwargs)
|
super().__init__(
|
||||||
|
rel=rel,
|
||||||
|
related_name=related_name,
|
||||||
|
related_query_name=related_query_name,
|
||||||
|
limit_choices_to=limit_choices_to,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
self.from_fields = from_fields
|
self.from_fields = from_fields
|
||||||
self.to_fields = to_fields
|
self.to_fields = to_fields
|
||||||
|
@ -825,6 +843,9 @@ class ForeignKey(ForeignObject):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
to,
|
to,
|
||||||
on_delete,
|
on_delete,
|
||||||
|
related_name=related_name,
|
||||||
|
related_query_name=related_query_name,
|
||||||
|
limit_choices_to=limit_choices_to,
|
||||||
from_fields=[RECURSIVE_RELATIONSHIP_CONSTANT],
|
from_fields=[RECURSIVE_RELATIONSHIP_CONSTANT],
|
||||||
to_fields=[to_field],
|
to_fields=[to_field],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
@ -1174,7 +1195,12 @@ class ManyToManyField(RelatedField):
|
||||||
)
|
)
|
||||||
self.has_null_arg = 'null' in kwargs
|
self.has_null_arg = 'null' in kwargs
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(
|
||||||
|
related_name=related_name,
|
||||||
|
related_query_name=related_query_name,
|
||||||
|
limit_choices_to=limit_choices_to,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
self.db_table = db_table
|
self.db_table = db_table
|
||||||
self.swappable = swappable
|
self.swappable = swappable
|
||||||
|
|
|
@ -387,6 +387,18 @@ custom middleware::
|
||||||
|
|
||||||
.. _Content-Security-Policy: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
.. _Content-Security-Policy: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||||
|
|
||||||
|
Migrations autodetector changes
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
The migrations autodetector now uses model states instead of model classes.
|
||||||
|
Also, migration operations for ``ForeignKey`` and ``ManyToManyField`` fields no
|
||||||
|
longer specify attributes which were not passed to the fields during
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
As a side-effect, running ``makemigrations`` might generate no-op
|
||||||
|
``AlterField`` operations for ``ManyToManyField`` and ``ForeignKey`` fields in
|
||||||
|
some cases.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -422,10 +434,6 @@ Miscellaneous
|
||||||
* Tests that fail to load, for example due to syntax errors, now always match
|
* Tests that fail to load, for example due to syntax errors, now always match
|
||||||
when using :option:`test --tag`.
|
when using :option:`test --tag`.
|
||||||
|
|
||||||
* The migrations autodetector now uses model states instead of model classes.
|
|
||||||
As a side-effect ``makemigrations`` might generate no-op ``AlterField``
|
|
||||||
operations for ``ForeignKey`` fields in some cases.
|
|
||||||
|
|
||||||
* The undocumented ``django.contrib.admin.utils.lookup_needs_distinct()``
|
* The undocumented ``django.contrib.admin.utils.lookup_needs_distinct()``
|
||||||
function is renamed to ``lookup_spawns_duplicates()``.
|
function is renamed to ``lookup_spawns_duplicates()``.
|
||||||
|
|
||||||
|
|
|
@ -432,6 +432,34 @@ class FieldDeconstructionTests(SimpleTestCase):
|
||||||
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
||||||
self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL")
|
self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL")
|
||||||
|
|
||||||
|
def test_many_to_many_field_related_name(self):
|
||||||
|
class MyModel(models.Model):
|
||||||
|
flag = models.BooleanField(default=True)
|
||||||
|
m2m = models.ManyToManyField('self')
|
||||||
|
m2m_related_name = models.ManyToManyField(
|
||||||
|
'self',
|
||||||
|
related_name='custom_name',
|
||||||
|
related_query_name='custom_query_name',
|
||||||
|
limit_choices_to={'flag': True},
|
||||||
|
)
|
||||||
|
|
||||||
|
name, path, args, kwargs = MyModel.m2m.field.deconstruct()
|
||||||
|
self.assertEqual(path, 'django.db.models.ManyToManyField')
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
# deconstruct() should not include attributes which were not passed to
|
||||||
|
# the field during initialization.
|
||||||
|
self.assertEqual(kwargs, {'to': 'field_deconstruction.MyModel'})
|
||||||
|
# Passed attributes.
|
||||||
|
name, path, args, kwargs = MyModel.m2m_related_name.field.deconstruct()
|
||||||
|
self.assertEqual(path, 'django.db.models.ManyToManyField')
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {
|
||||||
|
'to': 'field_deconstruction.MyModel',
|
||||||
|
'related_name': 'custom_name',
|
||||||
|
'related_query_name': 'custom_query_name',
|
||||||
|
'limit_choices_to': {'flag': True},
|
||||||
|
})
|
||||||
|
|
||||||
def test_positive_integer_field(self):
|
def test_positive_integer_field(self):
|
||||||
field = models.PositiveIntegerField()
|
field = models.PositiveIntegerField()
|
||||||
name, path, args, kwargs = field.deconstruct()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
|
|
@ -34,7 +34,12 @@ class CustomManyToManyField(RelatedField):
|
||||||
self.db_table = db_table
|
self.db_table = db_table
|
||||||
if kwargs['rel'].through is not None:
|
if kwargs['rel'].through is not None:
|
||||||
assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
|
assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
|
||||||
super().__init__(**kwargs)
|
super().__init__(
|
||||||
|
related_name=related_name,
|
||||||
|
related_query_name=related_query_name,
|
||||||
|
limit_choices_to=limit_choices_to,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name, **kwargs):
|
def contribute_to_class(self, cls, name, **kwargs):
|
||||||
if self.remote_field.symmetrical and (
|
if self.remote_field.symmetrical and (
|
||||||
|
|
Loading…
Reference in New Issue