summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Joseph <ajosephau@users.noreply.github.com>2024-12-10 06:55:18 +1100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2025-03-31 22:20:43 +0200
commit6888375c53476011754f778deabc6cdbfa327011 (patch)
treed709910e65f16a28f3886900c7a30f6fd33c5d30
parent9d5d0e8135a9654aa289cf922fcd00ad5e2a7fe5 (diff)
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>
-rw-r--r--django/db/models/fields/related.py22
-rw-r--r--docs/ref/checks.txt2
-rw-r--r--tests/invalid_models_tests/test_relative_fields.py26
3 files changed, 50 insertions, 0 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index dd4c09a4e3..bad71a5fd6 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -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
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index 6f7b7d271e..699a3d34c0 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -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
------
diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py
index 5a791d1fcc..82e5a954bd 100644
--- a/tests/invalid_models_tests/test_relative_fields.py
+++ b/tests/invalid_models_tests/test_relative_fields.py
@@ -1537,6 +1537,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):
class Model(models.Model):