Skip to content

Commit fcf9168

Browse files
cliffordgamajacobtylerwalls
authored andcommitted
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!
1 parent 04bcc99 commit fcf9168

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
@@ -348,6 +348,24 @@ def _check_clashes(self):
348348
)
349349
)
350350

351+
# Check clash between reverse accessor and manager names on
352+
# the target model.
353+
if not rel_is_hidden:
354+
manager_names = {m.name for m in rel_opts.managers}
355+
if rel_name in manager_names:
356+
errors.append(
357+
checks.Error(
358+
f"Related name '{rel_name}' for '{field_name}' "
359+
f"clashes with the name of a model manager.",
360+
hint=(
361+
"Rename the model manager or change the related_name "
362+
"argument in the definition for field '%s'." % field_name
363+
),
364+
obj=self,
365+
id="fields.E348",
366+
)
367+
)
368+
351369
return errors
352370

353371
def db_type(self, connection):
@@ -589,7 +607,6 @@ def check(self, **kwargs):
589607
*self._check_to_fields_exist(),
590608
*self._check_to_fields_composite_pk(),
591609
*self._check_unique_target(),
592-
*self._check_conflict_with_managers(),
593610
]
594611

595612
def _check_to_fields_exist(self):
@@ -719,27 +736,6 @@ def _check_unique_target(self):
719736
]
720737
return []
721738

722-
def _check_conflict_with_managers(self):
723-
errors = []
724-
manager_names = {manager.name for manager in self.opts.managers}
725-
for rel_objs in self.model._meta.related_objects:
726-
related_object_name = rel_objs.name
727-
if related_object_name in manager_names:
728-
field_name = f"{self.model._meta.object_name}.{self.name}"
729-
errors.append(
730-
checks.Error(
731-
f"Related name '{related_object_name}' for '{field_name}' "
732-
"clashes with the name of a model manager.",
733-
hint=(
734-
"Rename the model manager or change the related_name "
735-
f"argument in the definition for field '{field_name}'."
736-
),
737-
obj=self,
738-
id="fields.E348",
739-
)
740-
)
741-
return errors
742-
743739
def deconstruct(self):
744740
name, path, args, kwargs = super().deconstruct()
745741
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
@@ -1579,18 +1579,44 @@ class Author(models.Model):
15791579
Author.check(),
15801580
[
15811581
Error(
1582-
"Related name 'authors' for 'Author.mentor' clashes with the name "
1583-
"of a model manager.",
1582+
"Related name 'authors' for 'invalid_models_tests.Author.mentor' "
1583+
"clashes with the name of a model manager.",
15841584
hint=(
15851585
"Rename the model manager or change the related_name argument "
1586-
"in the definition for field 'Author.mentor'."
1586+
"in the definition for field "
1587+
"'invalid_models_tests.Author.mentor'."
15871588
),
15881589
obj=Author._meta.get_field("mentor"),
15891590
id="fields.E348",
15901591
)
15911592
],
15921593
)
15931594

1595+
def test_clash_between_rel_name_and_manager_non_self_referential(self):
1596+
class Thing(models.Model):
1597+
items = models.Manager()
1598+
1599+
class Item(models.Model):
1600+
thing = models.ForeignKey(Thing, models.CASCADE, related_name="items")
1601+
something = models.ForeignKey(Thing, models.CASCADE, related_name="+")
1602+
1603+
self.assertEqual(
1604+
Item.check(),
1605+
[
1606+
Error(
1607+
"Related name 'items' for 'invalid_models_tests.Item.thing' "
1608+
"clashes with the name of a model manager.",
1609+
hint=(
1610+
"Rename the model manager or change the related_name argument "
1611+
"in the definition for field 'invalid_models_tests.Item.thing'."
1612+
),
1613+
obj=Item._meta.get_field("thing"),
1614+
id="fields.E348",
1615+
)
1616+
],
1617+
)
1618+
self.assertEqual(Thing.check(), [])
1619+
15941620

15951621
@isolate_apps("invalid_models_tests")
15961622
class SelfReferentialM2MClashTests(SimpleTestCase):

0 commit comments

Comments
 (0)