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
|
kwargs["db_table"] = self.db_table
|
||||||
if self.remote_field.db_constraint is not True:
|
if self.remote_field.db_constraint is not True:
|
||||||
kwargs["db_constraint"] = self.remote_field.db_constraint
|
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):
|
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:
|
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 getattr(self.remote_field, "through", None) is not None:
|
||||||
if isinstance(self.remote_field.through, str):
|
if isinstance(self.remote_field.through, str):
|
||||||
kwargs["through"] = self.remote_field.through
|
kwargs["through"] = self.remote_field.through
|
||||||
|
|
|
@ -12,4 +12,6 @@ reformatted with `black`_.
|
||||||
Bugfixes
|
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()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(kwargs, {"to": "auth.Permission"})
|
self.assertEqual(kwargs, {"to": "auth.permission"})
|
||||||
self.assertFalse(hasattr(kwargs["to"], "setting_name"))
|
self.assertFalse(hasattr(kwargs["to"], "setting_name"))
|
||||||
# Test swappable
|
# Test swappable
|
||||||
field = models.ManyToManyField("auth.User")
|
field = models.ManyToManyField("auth.User")
|
||||||
name, path, args, kwargs = field.deconstruct()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(kwargs, {"to": "auth.User"})
|
self.assertEqual(kwargs, {"to": "auth.user"})
|
||||||
self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
|
self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
|
||||||
# Test through
|
# Test through
|
||||||
field = models.ManyToManyField("auth.Permission", through="auth.Group")
|
field = models.ManyToManyField("auth.Permission", through="auth.Group")
|
||||||
name, path, args, kwargs = field.deconstruct()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
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
|
# Test custom db_table
|
||||||
field = models.ManyToManyField("auth.Permission", db_table="custom_table")
|
field = models.ManyToManyField("auth.Permission", db_table="custom_table")
|
||||||
name, path, args, kwargs = field.deconstruct()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
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
|
# Test related_name
|
||||||
field = models.ManyToManyField("auth.Permission", related_name="custom_table")
|
field = models.ManyToManyField("auth.Permission", related_name="custom_table")
|
||||||
name, path, args, kwargs = field.deconstruct()
|
name, path, args, kwargs = field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
kwargs, {"to": "auth.Permission", "related_name": "custom_table"}
|
kwargs, {"to": "auth.permission", "related_name": "custom_table"}
|
||||||
)
|
)
|
||||||
# Test related_query_name
|
# Test related_query_name
|
||||||
field = models.ManyToManyField("auth.Permission", related_query_name="foobar")
|
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(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
kwargs, {"to": "auth.Permission", "related_query_name": "foobar"}
|
kwargs, {"to": "auth.permission", "related_query_name": "foobar"}
|
||||||
)
|
)
|
||||||
# Test limit_choices_to
|
# Test limit_choices_to
|
||||||
field = models.ManyToManyField(
|
field = models.ManyToManyField(
|
||||||
|
@ -520,7 +520,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(
|
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")
|
@override_settings(AUTH_USER_MODEL="auth.Permission")
|
||||||
|
@ -533,7 +533,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
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):
|
def test_many_to_many_field_related_name(self):
|
||||||
|
@ -551,7 +551,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
# deconstruct() should not include attributes which were not passed to
|
# deconstruct() should not include attributes which were not passed to
|
||||||
# the field during initialization.
|
# the field during initialization.
|
||||||
self.assertEqual(kwargs, {"to": "field_deconstruction.MyModel"})
|
self.assertEqual(kwargs, {"to": "field_deconstruction.mymodel"})
|
||||||
# Passed attributes.
|
# Passed attributes.
|
||||||
name, path, args, kwargs = MyModel.m2m_related_name.field.deconstruct()
|
name, path, args, kwargs = MyModel.m2m_related_name.field.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.ManyToManyField")
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
|
@ -559,7 +559,7 @@ class FieldDeconstructionTests(SimpleTestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
kwargs,
|
kwargs,
|
||||||
{
|
{
|
||||||
"to": "field_deconstruction.MyModel",
|
"to": "field_deconstruction.mymodel",
|
||||||
"related_query_name": "custom_query_name",
|
"related_query_name": "custom_query_name",
|
||||||
"limit_choices_to": {"flag": True},
|
"limit_choices_to": {"flag": True},
|
||||||
},
|
},
|
||||||
|
|
|
@ -3279,6 +3279,31 @@ class AutodetectorTests(TestCase):
|
||||||
[("__setting__", "AUTH_USER_MODEL")],
|
[("__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):
|
def test_swappable_changed(self):
|
||||||
with isolate_lru_cache(apps.get_swappable_settings_name):
|
with isolate_lru_cache(apps.get_swappable_settings_name):
|
||||||
before = self.make_project_state([self.custom_user, self.author_with_user])
|
before = self.make_project_state([self.custom_user, self.author_with_user])
|
||||||
|
|
Loading…
Reference in New Issue