1
0
mirror of https://github.com/django/django.git synced 2024-12-22 00:55:44 +00:00

Fixed #35992, Fixed #35997 -- Added system check for CompositePrimaryKeys in Meta.indexes/constraints/unique_together.

CompositePrimaryKeys are not supported in any of these options.
This commit is contained in:
Mariusz Felisiak 2024-12-11 21:37:23 +01:00 committed by Sarah Boyce
parent 322e49ba30
commit 2249370c86
4 changed files with 200 additions and 2 deletions

View File

@ -2288,6 +2288,16 @@ class Model(AltersData, metaclass=ModelBase):
id="models.E013",
)
)
elif isinstance(field, models.CompositePrimaryKey):
errors.append(
checks.Error(
f"{option!r} refers to a CompositePrimaryKey "
f"{field_name!r}, but CompositePrimaryKeys are not "
f"permitted in {option!r}.",
obj=cls,
id="models.E048",
)
)
elif field not in cls._meta.local_fields:
errors.append(
checks.Error(

View File

@ -93,11 +93,13 @@ class BaseConstraint:
return []
def _check_references(self, model, references):
from django.db.models.fields.composite import CompositePrimaryKey
errors = []
fields = set()
for field_name, *lookups in references:
# pk is an alias that won't be found by opts.get_field.
if field_name != "pk":
# pk is an alias that won't be found by opts.get_field().
if field_name != "pk" or isinstance(model._meta.pk, CompositePrimaryKey):
fields.add(field_name)
if not lookups:
# If it has no lookups it cannot result in a JOIN.

View File

@ -431,6 +431,9 @@ Models
(``db_table_comment``).
* **models.W047**: ``<database>`` does not support unique constraints with
nulls distinct.
* **models.E048**: ``constraints/indexes/unique_together`` refers to a
``CompositePrimaryKey`` ``<field name>``, but ``CompositePrimaryKey``\s are
not supported for that option.
Management Commands
-------------------

View File

@ -145,6 +145,27 @@ class UniqueTogetherTests(SimpleTestCase):
self.assertEqual(Bar.check(), [])
def test_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
unique_together = [["pk"]]
self.assertEqual(
Model.check(),
[
Error(
"'unique_together' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'unique_together'.",
obj=Model,
id="models.E048",
),
],
)
@isolate_apps("invalid_models_tests")
class IndexesTests(TestCase):
@ -225,6 +246,27 @@ class IndexesTests(TestCase):
self.assertEqual(Bar.check(), [])
def test_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
indexes = [models.Index(fields=["pk", "name"], name="name")]
self.assertEqual(
Model.check(),
[
Error(
"'indexes' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'indexes'.",
obj=Model,
id="models.E048",
),
],
)
def test_name_constraints(self):
class Model(models.Model):
class Meta:
@ -446,6 +488,28 @@ class IndexesTests(TestCase):
self.assertEqual(Model.check(databases=self.databases), [])
@skipUnlessDBFeature("supports_covering_indexes")
def test_index_include_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
indexes = [models.Index(fields=["name"], include=["pk"], name="name")]
self.assertEqual(
Model.check(),
[
Error(
"'indexes' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'indexes'.",
obj=Model,
id="models.E048",
),
],
)
def test_func_index(self):
class Model(models.Model):
name = models.CharField(max_length=10)
@ -581,6 +645,27 @@ class IndexesTests(TestCase):
self.assertEqual(Bar.check(), [])
def test_func_index_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
indexes = [models.Index(Abs("pk"), name="name")]
self.assertEqual(
Model.check(),
[
Error(
"'indexes' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'indexes'.",
obj=Model,
id="models.E048",
),
],
)
@isolate_apps("invalid_models_tests")
class FieldNamesTests(TestCase):
@ -2209,6 +2294,33 @@ class ConstraintsTests(TestCase):
)
self.assertEqual(Model.check(databases=self.databases), expected_warnings)
@skipUnlessDBFeature("supports_table_check_constraints")
def test_check_constraint_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
constraints = [
models.CheckConstraint(
name="name",
condition=models.Q(pk__gt=(7, "focal")),
),
]
self.assertEqual(
Model.check(databases=self.databases),
[
Error(
"'constraints' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'constraints'.",
obj=Model,
id="models.E048",
),
],
)
def test_unique_constraint_with_condition(self):
class Model(models.Model):
age = models.IntegerField()
@ -2471,6 +2583,27 @@ class ConstraintsTests(TestCase):
self.assertEqual(Model.check(databases=self.databases), [])
def test_unique_constraint_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
constraints = [models.UniqueConstraint(fields=["pk"], name="name")]
self.assertEqual(
Model.check(databases=self.databases),
[
Error(
"'constraints' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'constraints'.",
obj=Model,
id="models.E048",
),
],
)
def test_unique_constraint_with_include(self):
class Model(models.Model):
age = models.IntegerField()
@ -2618,6 +2751,34 @@ class ConstraintsTests(TestCase):
self.assertEqual(Model.check(databases=self.databases), [])
@skipUnlessDBFeature("supports_covering_indexes")
def test_unique_constraint_include_pointing_to_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["version"],
include=["pk"],
name="name",
),
]
self.assertEqual(
Model.check(databases=self.databases),
[
Error(
"'constraints' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'constraints'.",
obj=Model,
id="models.E048",
),
],
)
def test_func_unique_constraint(self):
class Model(models.Model):
name = models.CharField(max_length=10)
@ -2815,3 +2976,25 @@ class ConstraintsTests(TestCase):
]
self.assertEqual(Bar.check(databases=self.databases), [])
@skipUnlessDBFeature("supports_expression_indexes")
def test_func_unique_constraint_pointing_composite_primary_key(self):
class Model(models.Model):
pk = models.CompositePrimaryKey("version", "name")
version = models.IntegerField()
name = models.CharField(max_length=20)
class Meta:
constraints = [models.UniqueConstraint(Abs("pk"), name="name")]
self.assertEqual(
Model.check(databases=self.databases),
[
Error(
"'constraints' refers to a CompositePrimaryKey 'pk', but "
"CompositePrimaryKeys are not permitted in 'constraints'.",
obj=Model,
id="models.E048",
),
],
)