From 6888375c53476011754f778deabc6cdbfa327011 Mon Sep 17 00:00:00 2001
From: Anthony Joseph <ajosephau@users.noreply.github.com>
Date: Tue, 10 Dec 2024 06:55:18 +1100
Subject: [PATCH] Fixed #22977 -- Added system check for clashing managers and
 reverse related fields.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
---
 django/db/models/fields/related.py            | 22 ++++++++++++++++
 docs/ref/checks.txt                           |  2 ++
 .../test_relative_fields.py                   | 26 +++++++++++++++++++
 3 files changed, 50 insertions(+)

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
@@ -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):