diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index a59dcac68c..9ad440128e 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -626,9 +626,10 @@ class ForeignObject(RelatedField): if isinstance(field, CompositePrimaryKey): errors.append( checks.Error( - "Field defines a relation to the CompositePrimaryKey of " - f"model {self.remote_field.model._meta.object_name!r} " - "which is not supported.", + "Field defines a relation involving model " + f"{self.remote_field.model._meta.object_name!r} which has " + "a CompositePrimaryKey and such relations are not " + "supported.", obj=self, id="fields.E347", ) @@ -1538,20 +1539,24 @@ class ManyToManyField(RelatedField): to_model_name = to_model else: to_model_name = to_model._meta.object_name - if ( - self.remote_field.through_fields is None - and not isinstance(to_model, str) - and isinstance(to_model._meta.pk, CompositePrimaryKey) + if self.remote_field.through_fields is None and not isinstance( + to_model, str ): - errors.append( - checks.Error( - "Field defines a relation to the CompositePrimaryKey of model " - f"{self.remote_field.model._meta.object_name!r} which is not " - "supported.", - obj=self, - id="fields.E347", + model_name = None + if isinstance(to_model._meta.pk, CompositePrimaryKey): + model_name = self.remote_field.model._meta.object_name + elif isinstance(from_model._meta.pk, CompositePrimaryKey): + model_name = from_model_name + if model_name: + errors.append( + checks.Error( + f"Field defines a relation involving model {model_name!r} " + "which has a CompositePrimaryKey and such relations are " + "not supported.", + obj=self, + id="fields.E347", + ) ) - ) relationship_model_name = self.remote_field.through._meta.object_name self_referential = from_model == to_model # Count foreign keys in intermediate model diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index bb54dbdb98..51f6c282c6 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -338,8 +338,8 @@ Related fields * **fields.W345**: ``related_name`` has no effect on ``ManyToManyField`` with a symmetrical relationship, e.g. to "self". * **fields.W346**: ``db_comment`` has no effect on ``ManyToManyField``. -* **fields.E347**: Field defines a relation to the ``CompositePrimaryKey`` of - model ```` which is not supported. +* **fields.E347**: Field defines a relation involving model ```` which + has a ``CompositePrimaryKey`` and such relations are not supported. * **fields.E348**: Related name ```` for ``.`` clashes with the name of a model manager. diff --git a/docs/releases/5.2.5.txt b/docs/releases/5.2.5.txt index 6791983aad..f782ea9632 100644 --- a/docs/releases/5.2.5.txt +++ b/docs/releases/5.2.5.txt @@ -21,3 +21,7 @@ Bugfixes (:ticket:`36518`). * Added compatibility for ``docutils`` 0.22 (:ticket:`36535`). + +* Fixed a crash in Django 5.2 when using a ``ManyToManyField`` on a model with + a composite primary key, by extending the ``fields.E347`` system check + (:ticket:`36530`). diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py index 82e5a954bd..ed6d39f7c6 100644 --- a/tests/invalid_models_tests/test_relative_fields.py +++ b/tests/invalid_models_tests/test_relative_fields.py @@ -454,29 +454,19 @@ class RelativeFieldTests(SimpleTestCase): "Parent", on_delete=models.CASCADE, related_name="child_string_set" ) + error = ( + "Field defines a relation involving model 'Parent' which has a " + "CompositePrimaryKey and such relations are not supported." + ) field = Child._meta.get_field("rel_string_parent") self.assertEqual( field.check(), - [ - Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", - obj=field, - id="fields.E347", - ), - ], + [Error(error, obj=field, id="fields.E347")], ) field = Child._meta.get_field("rel_class_parent") self.assertEqual( field.check(), - [ - Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", - obj=field, - id="fields.E347", - ), - ], + [Error(error, obj=field, id="fields.E347")], ) def test_many_to_many_to_model_with_composite_primary_key(self): @@ -493,29 +483,45 @@ class RelativeFieldTests(SimpleTestCase): "Parent", related_name="child_string_set" ) + error = ( + "Field defines a relation involving model 'Parent' which has a " + "CompositePrimaryKey and such relations are not supported." + ) field = Child._meta.get_field("rel_string_parent") self.assertEqual( field.check(from_model=Child), - [ - Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", - obj=field, - id="fields.E347", - ), - ], + [Error(error, obj=field, id="fields.E347")], ) field = Child._meta.get_field("rel_class_parent") self.assertEqual( field.check(from_model=Child), - [ - Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", - obj=field, - id="fields.E347", - ), - ], + [Error(error, obj=field, id="fields.E347")], + ) + + def test_many_to_many_from_model_with_composite_primary_key(self): + class Parent(models.Model): + name = models.CharField(max_length=20) + + class Meta: + app_label = "invalid_models_tests" + + class Child(models.Model): + pk = models.CompositePrimaryKey("version", "name") + version = models.IntegerField() + name = models.CharField(max_length=20) + parents = models.ManyToManyField(Parent) + + class Meta: + app_label = "invalid_models_tests" + + error = ( + "Field defines a relation involving model 'Child' which has a " + "CompositePrimaryKey and such relations are not supported." + ) + field = Child._meta.get_field("parents") + self.assertEqual( + field.check(from_model=Child), + [Error(error, obj=field, id="fields.E347")], ) def test_foreign_key_to_non_unique_field(self): @@ -1038,8 +1044,8 @@ class RelativeFieldTests(SimpleTestCase): field.check(), [ Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", + "Field defines a relation involving model 'Parent' which has a " + "CompositePrimaryKey and such relations are not supported.", obj=field, id="fields.E347", ), @@ -1060,8 +1066,8 @@ class RelativeFieldTests(SimpleTestCase): field.check(), [ Error( - "Field defines a relation to the CompositePrimaryKey of model " - "'Parent' which is not supported.", + "Field defines a relation involving model 'Parent' which has a " + "CompositePrimaryKey and such relations are not supported.", obj=field, id="fields.E347", ),