mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +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:
parent
322e49ba30
commit
2249370c86
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
-------------------
|
||||
|
@ -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",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user