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_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
|
||||
def related_model(self):
|
||||
# Can't cache this property until all the models are loaded.
|
||||
|
@ -319,12 +331,12 @@ class RelatedField(FieldCacheMixin, Field):
|
|||
|
||||
def deconstruct(self):
|
||||
name, path, args, kwargs = super().deconstruct()
|
||||
if self.remote_field.limit_choices_to:
|
||||
kwargs['limit_choices_to'] = self.remote_field.limit_choices_to
|
||||
if self.remote_field.related_name is not None:
|
||||
kwargs['related_name'] = self.remote_field.related_name
|
||||
if self.remote_field.related_query_name is not None:
|
||||
kwargs['related_query_name'] = self.remote_field.related_query_name
|
||||
if self._limit_choices_to:
|
||||
kwargs['limit_choices_to'] = self._limit_choices_to
|
||||
if self._related_name is not None:
|
||||
kwargs['related_name'] = self._related_name
|
||||
if self._related_query_name is not None:
|
||||
kwargs['related_query_name'] = self._related_query_name
|
||||
return name, path, args, kwargs
|
||||
|
||||
def get_forward_related_filter(self, obj):
|
||||
|
@ -471,7 +483,13 @@ class ForeignObject(RelatedField):
|
|||
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.to_fields = to_fields
|
||||
|
@ -825,6 +843,9 @@ class ForeignKey(ForeignObject):
|
|||
super().__init__(
|
||||
to,
|
||||
on_delete,
|
||||
related_name=related_name,
|
||||
related_query_name=related_query_name,
|
||||
limit_choices_to=limit_choices_to,
|
||||
from_fields=[RECURSIVE_RELATIONSHIP_CONSTANT],
|
||||
to_fields=[to_field],
|
||||
**kwargs,
|
||||
|
@ -1174,7 +1195,12 @@ class ManyToManyField(RelatedField):
|
|||
)
|
||||
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.swappable = swappable
|
||||
|
|
|
@ -387,6 +387,18 @@ custom middleware::
|
|||
|
||||
.. _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
|
||||
-------------
|
||||
|
||||
|
@ -422,10 +434,6 @@ Miscellaneous
|
|||
* Tests that fail to load, for example due to syntax errors, now always match
|
||||
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()``
|
||||
function is renamed to ``lookup_spawns_duplicates()``.
|
||||
|
||||
|
|
|
@ -432,6 +432,34 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
||||
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):
|
||||
field = models.PositiveIntegerField()
|
||||
name, path, args, kwargs = field.deconstruct()
|
||||
|
|
|
@ -34,7 +34,12 @@ class CustomManyToManyField(RelatedField):
|
|||
self.db_table = db_table
|
||||
if kwargs['rel'].through is not None:
|
||||
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):
|
||||
if self.remote_field.symmetrical and (
|
||||
|
|
Loading…
Reference in New Issue