Skip to content

Commit 4eb38f6

Browse files
cliffordgamajacobtylerwalls
authored andcommitted
[6.0.x] Fixed #36973 -- Made fields.E348 check detect further clashes between managers and related_names.
Clashes were only detected for self-referential relationships, i.e. ForeignKey("self"). Refs #22977. Bug in 6888375. Thanks JaeHyuckSa for the thorough review! Backport of fcf9168 from main.
1 parent 640c431 commit 4eb38f6

3 files changed

Lines changed: 52 additions & 25 deletions

File tree

django/db/models/fields/related.py

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,24 @@ def _check_clashes(self):
339339
)
340340
)
341341

342+
# Check clash between reverse accessor and manager names on
343+
# the target model.
344+
if not rel_is_hidden:
345+
manager_names = {m.name for m in rel_opts.managers}
346+
if rel_name in manager_names:
347+
errors.append(
348+
checks.Error(
349+
f"Related name '{rel_name}' for '{field_name}' "
350+
f"clashes with the name of a model manager.",
351+
hint=(
352+
"Rename the model manager or change the related_name "
353+
"argument in the definition for field '%s'." % field_name
354+
),
355+
obj=self,
356+
id="fields.E348",
357+
)
358+
)
359+
342360
return errors
343361

344362
def db_type(self, connection):
@@ -580,7 +598,6 @@ def check(self, **kwargs):
580598
*self._check_to_fields_exist(),
581599
*self._check_to_fields_composite_pk(),
582600
*self._check_unique_target(),
583-
*self._check_conflict_with_managers(),
584601
]
585602

586603
def _check_to_fields_exist(self):
@@ -710,27 +727,6 @@ def _check_unique_target(self):
710727
]
711728
return []
712729

713-
def _check_conflict_with_managers(self):
714-
errors = []
715-
manager_names = {manager.name for manager in self.opts.managers}
716-
for rel_objs in self.model._meta.related_objects:
717-
related_object_name = rel_objs.name
718-
if related_object_name in manager_names:
719-
field_name = f"{self.model._meta.object_name}.{self.name}"
720-
errors.append(
721-
checks.Error(
722-
f"Related name '{related_object_name}' for '{field_name}' "
723-
"clashes with the name of a model manager.",
724-
hint=(
725-
"Rename the model manager or change the related_name "
726-
f"argument in the definition for field '{field_name}'."
727-
),
728-
obj=self,
729-
id="fields.E348",
730-
)
731-
)
732-
return errors
733-
734730
def deconstruct(self):
735731
name, path, args, kwargs = super().deconstruct()
736732
kwargs["on_delete"] = self.remote_field.on_delete

docs/releases/6.0.4.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,8 @@ Bugfixes
1818
* Fixed a regression in Django 6.0 in admin forms where
1919
``RelatedFieldWidgetWrapper`` incorrectly wrapped all widgets in a
2020
``<fieldset>`` (:ticket:`36949`).
21+
22+
* Fixed a bug in Django 6.0 where the ``fields.E348`` system check did not
23+
detect name clashes between model managers and
24+
:attr:`~django.db.models.ForeignKey.related_name`\s for non-self-referential
25+
relationships (:ticket:`36973`).

tests/invalid_models_tests/test_relative_fields.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,18 +1555,44 @@ class Author(models.Model):
15551555
Author.check(),
15561556
[
15571557
Error(
1558-
"Related name 'authors' for 'Author.mentor' clashes with the name "
1559-
"of a model manager.",
1558+
"Related name 'authors' for 'invalid_models_tests.Author.mentor' "
1559+
"clashes with the name of a model manager.",
15601560
hint=(
15611561
"Rename the model manager or change the related_name argument "
1562-
"in the definition for field 'Author.mentor'."
1562+
"in the definition for field "
1563+
"'invalid_models_tests.Author.mentor'."
15631564
),
15641565
obj=Author._meta.get_field("mentor"),
15651566
id="fields.E348",
15661567
)
15671568
],
15681569
)
15691570

1571+
def test_clash_between_rel_name_and_manager_non_self_referential(self):
1572+
class Thing(models.Model):
1573+
items = models.Manager()
1574+
1575+
class Item(models.Model):
1576+
thing = models.ForeignKey(Thing, models.CASCADE, related_name="items")
1577+
something = models.ForeignKey(Thing, models.CASCADE, related_name="+")
1578+
1579+
self.assertEqual(
1580+
Item.check(),
1581+
[
1582+
Error(
1583+
"Related name 'items' for 'invalid_models_tests.Item.thing' "
1584+
"clashes with the name of a model manager.",
1585+
hint=(
1586+
"Rename the model manager or change the related_name argument "
1587+
"in the definition for field 'invalid_models_tests.Item.thing'."
1588+
),
1589+
obj=Item._meta.get_field("thing"),
1590+
id="fields.E348",
1591+
)
1592+
],
1593+
)
1594+
self.assertEqual(Thing.check(), [])
1595+
15701596

15711597
@isolate_apps("invalid_models_tests")
15721598
class SelfReferentialM2MClashTests(SimpleTestCase):

0 commit comments

Comments
 (0)