Fixed #33515 -- Prevented recreation of migration for ManyToManyField to lowercased swappable setting.
Thanks Chris Lee for the report.
Regression in 4328970780
.
Refs #23916.
This commit is contained in:
parent
35c2474f16
commit
1e2e1be02b
|
@ -1728,11 +1728,15 @@ class ManyToManyField(RelatedField):
|
|||
kwargs["db_table"] = self.db_table
|
||||
if self.remote_field.db_constraint is not True:
|
||||
kwargs["db_constraint"] = self.remote_field.db_constraint
|
||||
# Rel needs more work.
|
||||
# Lowercase model names as they should be treated as case-insensitive.
|
||||
if isinstance(self.remote_field.model, str):
|
||||
kwargs["to"] = self.remote_field.model
|
||||
if "." in self.remote_field.model:
|
||||
app_label, model_name = self.remote_field.model.split(".")
|
||||
kwargs["to"] = "%s.%s" % (app_label, model_name.lower())
|
||||
else:
|
||||
kwargs["to"] = self.remote_field.model.lower()
|
||||
else:
|
||||
kwargs["to"] = self.remote_field.model._meta.label
|
||||
kwargs["to"] = self.remote_field.model._meta.label_lower
|
||||
if getattr(self.remote_field, "through", None) is not None:
|
||||
if isinstance(self.remote_field.through, str):
|
||||
kwargs["through"] = self.remote_field.through
|
||||
|
|
|
@ -12,4 +12,6 @@ reformatted with `black`_.
|
|||
Bugfixes
|
||||
========
|
||||
|
||||
* ...
|
||||
* Prevented, following a regression in Django 4.0.1, :djadmin:`makemigrations`
|
||||
from generating infinite migrations for a model with ``ManyToManyField`` to
|
||||
a lowercased swappable model such as ``'auth.user'`` (:ticket:`33515`).
|
||||
|
|
|
@ -475,34 +475,34 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
name, path, args, kwargs = field.deconstruct()
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
||||
self.assertEqual(kwargs, {"to": "auth.permission"})
|
||||
self.assertFalse(hasattr(kwargs["to"], "setting_name"))
|
||||
# Test swappable
|
||||
field = models.ManyToManyField("auth.User")
|
||||
name, path, args, kwargs = field.deconstruct()
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(kwargs, {"to": "auth.User"})
|
||||
self.assertEqual(kwargs, {"to": "auth.user"})
|
||||
self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
|
||||
# Test through
|
||||
field = models.ManyToManyField("auth.Permission", through="auth.Group")
|
||||
name, path, args, kwargs = field.deconstruct()
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(kwargs, {"to": "auth.Permission", "through": "auth.Group"})
|
||||
self.assertEqual(kwargs, {"to": "auth.permission", "through": "auth.Group"})
|
||||
# Test custom db_table
|
||||
field = models.ManyToManyField("auth.Permission", db_table="custom_table")
|
||||
name, path, args, kwargs = field.deconstruct()
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(kwargs, {"to": "auth.Permission", "db_table": "custom_table"})
|
||||
self.assertEqual(kwargs, {"to": "auth.permission", "db_table": "custom_table"})
|
||||
# Test related_name
|
||||
field = models.ManyToManyField("auth.Permission", related_name="custom_table")
|
||||
name, path, args, kwargs = field.deconstruct()
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(
|
||||
kwargs, {"to": "auth.Permission", "related_name": "custom_table"}
|
||||
kwargs, {"to": "auth.permission", "related_name": "custom_table"}
|
||||
)
|
||||
# Test related_query_name
|
||||
field = models.ManyToManyField("auth.Permission", related_query_name="foobar")
|
||||
|
@ -510,7 +510,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(
|
||||
kwargs, {"to": "auth.Permission", "related_query_name": "foobar"}
|
||||
kwargs, {"to": "auth.permission", "related_query_name": "foobar"}
|
||||
)
|
||||
# Test limit_choices_to
|
||||
field = models.ManyToManyField(
|
||||
|
@ -520,7 +520,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(
|
||||
kwargs, {"to": "auth.Permission", "limit_choices_to": {"foo": "bar"}}
|
||||
kwargs, {"to": "auth.permission", "limit_choices_to": {"foo": "bar"}}
|
||||
)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL="auth.Permission")
|
||||
|
@ -533,7 +533,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
|
||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||
self.assertEqual(args, [])
|
||||
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
||||
self.assertEqual(kwargs, {"to": "auth.permission"})
|
||||
self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
|
||||
|
||||
def test_many_to_many_field_related_name(self):
|
||||
|
@ -551,7 +551,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
self.assertEqual(args, [])
|
||||
# deconstruct() should not include attributes which were not passed to
|
||||
# the field during initialization.
|
||||
self.assertEqual(kwargs, {"to": "field_deconstruction.MyModel"})
|
||||
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")
|
||||
|
@ -559,7 +559,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
|||
self.assertEqual(
|
||||
kwargs,
|
||||
{
|
||||
"to": "field_deconstruction.MyModel",
|
||||
"to": "field_deconstruction.mymodel",
|
||||
"related_query_name": "custom_query_name",
|
||||
"limit_choices_to": {"flag": True},
|
||||
},
|
||||
|
|
|
@ -3279,6 +3279,31 @@ class AutodetectorTests(TestCase):
|
|||
[("__setting__", "AUTH_USER_MODEL")],
|
||||
)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
|
||||
def test_swappable_many_to_many_model_case(self):
|
||||
document_lowercase = ModelState(
|
||||
"testapp",
|
||||
"Document",
|
||||
[
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("owners", models.ManyToManyField(settings.AUTH_USER_MODEL.lower())),
|
||||
],
|
||||
)
|
||||
document = ModelState(
|
||||
"testapp",
|
||||
"Document",
|
||||
[
|
||||
("id", models.AutoField(primary_key=True)),
|
||||
("owners", models.ManyToManyField(settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
)
|
||||
with isolate_lru_cache(apps.get_swappable_settings_name):
|
||||
changes = self.get_changes(
|
||||
[self.custom_user, document_lowercase],
|
||||
[self.custom_user, document],
|
||||
)
|
||||
self.assertEqual(len(changes), 0)
|
||||
|
||||
def test_swappable_changed(self):
|
||||
with isolate_lru_cache(apps.get_swappable_settings_name):
|
||||
before = self.make_project_state([self.custom_user, self.author_with_user])
|
||||
|
|
Loading…
Reference in New Issue