1
0
mirror of https://github.com/django/django.git synced 2025-04-04 13:36:42 +00:00

Fixed #22977 -- Added system check for clashing managers and reverse related fields.

With thanks to  Konrad Świat, Loïc Bistuer, Russell Keith-Magee,
and Mariusz Felisiak.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
Anthony Joseph 2024-12-10 06:55:18 +11:00 committed by Mariusz Felisiak
parent 9d5d0e8135
commit 6888375c53
3 changed files with 50 additions and 0 deletions

View File

@ -580,6 +580,7 @@ class ForeignObject(RelatedField):
*self._check_to_fields_exist(),
*self._check_to_fields_composite_pk(),
*self._check_unique_target(),
*self._check_conflict_with_managers(),
]
def _check_to_fields_exist(self):
@ -708,6 +709,27 @@ class ForeignObject(RelatedField):
]
return []
def _check_conflict_with_managers(self):
errors = []
manager_names = {manager.name for manager in self.opts.managers}
for rel_objs in self.model._meta.related_objects:
related_object_name = rel_objs.name
if related_object_name in manager_names:
field_name = f"{self.model._meta.object_name}.{self.name}"
errors.append(
checks.Error(
f"Related name '{related_object_name}' for '{field_name}' "
"clashes with the name of a model manager.",
hint=(
"Rename the model manager or change the related_name "
f"argument in the definition for field '{field_name}'."
),
obj=self,
id="fields.E348",
)
)
return errors
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
kwargs["on_delete"] = self.remote_field.on_delete

View File

@ -340,6 +340,8 @@ Related fields
* **fields.W346**: ``db_comment`` has no effect on ``ManyToManyField``.
* **fields.E347**: Field defines a relation to the ``CompositePrimaryKey`` of
model ``<model>`` which is not supported.
* **fields.E348**: Related name ``<related_name>`` for ``<model>.<field name>``
clashes with the name of a model manager.
Models
------

View File

@ -1536,6 +1536,32 @@ class ExplicitRelatedQueryNameClashTests(SimpleTestCase):
)
@isolate_apps("invalid_models_tests")
class RelatedQueryNameClashWithManagerTests(SimpleTestCase):
def test_clash_between_related_query_name_and_manager(self):
class Author(models.Model):
authors = models.Manager()
mentor = models.ForeignKey(
"self", related_name="authors", on_delete=models.CASCADE
)
self.assertEqual(
Author.check(),
[
Error(
"Related name 'authors' for 'Author.mentor' clashes with the name "
"of a model manager.",
hint=(
"Rename the model manager or change the related_name argument "
"in the definition for field 'Author.mentor'."
),
obj=Author._meta.get_field("mentor"),
id="fields.E348",
)
],
)
@isolate_apps("invalid_models_tests")
class SelfReferentialM2MClashTests(SimpleTestCase):
def test_clash_between_accessors(self):