summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjkhall81 <jason.kei.hall@gmail.com>2025-07-28 08:59:07 -0700
committernessita <124304+nessita@users.noreply.github.com>2025-08-05 08:34:40 -0300
commit2013092b693be0ebdf36f41dc61615a2de1bbe31 (patch)
tree1fb5031596c477a7aa8ea87b4dd0e27b6961f918
parente664c5afa934be5a7b57576de12620071077c4fb (diff)
Fixed #36530 -- Extended fields.E347 to check for ManyToManyField involving CompositePrimaryKey on either side.
Thanks to Jacob Walls for the report.
-rw-r--r--django/db/models/fields/related.py35
-rw-r--r--docs/ref/checks.txt4
-rw-r--r--docs/releases/5.2.5.txt4
-rw-r--r--tests/invalid_models_tests/test_relative_fields.py78
4 files changed, 68 insertions, 53 deletions
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 ``<model>`` which is not supported.
+* **fields.E347**: Field defines a relation involving model ``<model>`` which
+ has a ``CompositePrimaryKey`` and such relations are not supported.
* **fields.E348**: Related name ``<related_name>`` for ``<model>.<field name>``
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",
),