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",
|
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:
|
elif field not in cls._meta.local_fields:
|
||||||
errors.append(
|
errors.append(
|
||||||
checks.Error(
|
checks.Error(
|
||||||
|
@ -93,11 +93,13 @@ class BaseConstraint:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def _check_references(self, model, references):
|
def _check_references(self, model, references):
|
||||||
|
from django.db.models.fields.composite import CompositePrimaryKey
|
||||||
|
|
||||||
errors = []
|
errors = []
|
||||||
fields = set()
|
fields = set()
|
||||||
for field_name, *lookups in references:
|
for field_name, *lookups in references:
|
||||||
# pk is an alias that won't be found by opts.get_field.
|
# pk is an alias that won't be found by opts.get_field().
|
||||||
if field_name != "pk":
|
if field_name != "pk" or isinstance(model._meta.pk, CompositePrimaryKey):
|
||||||
fields.add(field_name)
|
fields.add(field_name)
|
||||||
if not lookups:
|
if not lookups:
|
||||||
# If it has no lookups it cannot result in a JOIN.
|
# If it has no lookups it cannot result in a JOIN.
|
||||||
|
@ -431,6 +431,9 @@ Models
|
|||||||
(``db_table_comment``).
|
(``db_table_comment``).
|
||||||
* **models.W047**: ``<database>`` does not support unique constraints with
|
* **models.W047**: ``<database>`` does not support unique constraints with
|
||||||
nulls distinct.
|
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
|
Management Commands
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -145,6 +145,27 @@ class UniqueTogetherTests(SimpleTestCase):
|
|||||||
|
|
||||||
self.assertEqual(Bar.check(), [])
|
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")
|
@isolate_apps("invalid_models_tests")
|
||||||
class IndexesTests(TestCase):
|
class IndexesTests(TestCase):
|
||||||
@ -225,6 +246,27 @@ class IndexesTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(Bar.check(), [])
|
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):
|
def test_name_constraints(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -446,6 +488,28 @@ class IndexesTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(Model.check(databases=self.databases), [])
|
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):
|
def test_func_index(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
@ -581,6 +645,27 @@ class IndexesTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(Bar.check(), [])
|
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")
|
@isolate_apps("invalid_models_tests")
|
||||||
class FieldNamesTests(TestCase):
|
class FieldNamesTests(TestCase):
|
||||||
@ -2209,6 +2294,33 @@ class ConstraintsTests(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(Model.check(databases=self.databases), expected_warnings)
|
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):
|
def test_unique_constraint_with_condition(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
age = models.IntegerField()
|
age = models.IntegerField()
|
||||||
@ -2471,6 +2583,27 @@ class ConstraintsTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(Model.check(databases=self.databases), [])
|
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):
|
def test_unique_constraint_with_include(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
age = models.IntegerField()
|
age = models.IntegerField()
|
||||||
@ -2618,6 +2751,34 @@ class ConstraintsTests(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(Model.check(databases=self.databases), [])
|
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):
|
def test_func_unique_constraint(self):
|
||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
@ -2815,3 +2976,25 @@ class ConstraintsTests(TestCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(Bar.check(databases=self.databases), [])
|
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