summaryrefslogtreecommitdiff
path: root/tests/invalid_models_tests
diff options
context:
space:
mode:
authorHannes Ljungberg <hannes.ljungberg@gmail.com>2021-02-06 20:45:54 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-02-23 20:19:53 +0100
commit3aa545281e0c0f9fac93753e3769df9e0334dbaa (patch)
tree454bec922a78df9ecfe6eab197e7750e8a5bafac /tests/invalid_models_tests
parent19ce1d493ae7cbc1e704d338077b1f5f5e5769c9 (diff)
downloaddjango-3aa545281e0c0f9fac93753e3769df9e0334dbaa.tar.gz
Fixed #30916 -- Added support for functional unique constraints.
Thanks Ian Foote and Mariusz Felisiak for reviews.
Diffstat (limited to 'tests/invalid_models_tests')
-rw-r--r--tests/invalid_models_tests/test_models.py141
1 files changed, 141 insertions, 0 deletions
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py
index b20eef7159..c79684487d 100644
--- a/tests/invalid_models_tests/test_models.py
+++ b/tests/invalid_models_tests/test_models.py
@@ -2178,3 +2178,144 @@ class ConstraintsTests(TestCase):
]
self.assertEqual(Model.check(databases=self.databases), [])
+
+ def test_func_unique_constraint(self):
+ class Model(models.Model):
+ name = models.CharField(max_length=10)
+
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(Lower('name'), name='lower_name_uq'),
+ ]
+
+ warn = Warning(
+ '%s does not support unique constraints on expressions.'
+ % connection.display_name,
+ hint=(
+ "A constraint won't be created. Silence this warning if you "
+ "don't care about it."
+ ),
+ obj=Model,
+ id='models.W044',
+ )
+ expected = [] if connection.features.supports_expression_indexes else [warn]
+ self.assertEqual(Model.check(databases=self.databases), expected)
+
+ def test_func_unique_constraint_required_db_features(self):
+ class Model(models.Model):
+ name = models.CharField(max_length=10)
+
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(Lower('name'), name='lower_name_unq'),
+ ]
+ required_db_features = {'supports_expression_indexes'}
+
+ self.assertEqual(Model.check(databases=self.databases), [])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_expression_custom_lookup(self):
+ class Model(models.Model):
+ height = models.IntegerField()
+ weight = models.IntegerField()
+
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(
+ models.F('height') / (models.F('weight__abs') + models.Value(5)),
+ name='name',
+ ),
+ ]
+
+ with register_lookup(models.IntegerField, Abs):
+ self.assertEqual(Model.check(databases=self.databases), [])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_pointing_to_missing_field(self):
+ class Model(models.Model):
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(Lower('missing_field').desc(), name='name'),
+ ]
+
+ self.assertEqual(Model.check(databases=self.databases), [
+ Error(
+ "'constraints' refers to the nonexistent field "
+ "'missing_field'.",
+ obj=Model,
+ id='models.E012',
+ ),
+ ])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_pointing_to_missing_field_nested(self):
+ class Model(models.Model):
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(Abs(Round('missing_field')), name='name'),
+ ]
+
+ self.assertEqual(Model.check(databases=self.databases), [
+ Error(
+ "'constraints' refers to the nonexistent field "
+ "'missing_field'.",
+ obj=Model,
+ id='models.E012',
+ ),
+ ])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_pointing_to_m2m_field(self):
+ class Model(models.Model):
+ m2m = models.ManyToManyField('self')
+
+ class Meta:
+ constraints = [models.UniqueConstraint(Lower('m2m'), name='name')]
+
+ self.assertEqual(Model.check(databases=self.databases), [
+ Error(
+ "'constraints' refers to a ManyToManyField 'm2m', but "
+ "ManyToManyFields are not permitted in 'constraints'.",
+ obj=Model,
+ id='models.E013',
+ ),
+ ])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_pointing_to_non_local_field(self):
+ class Foo(models.Model):
+ field1 = models.CharField(max_length=15)
+
+ class Bar(Foo):
+ class Meta:
+ constraints = [models.UniqueConstraint(Lower('field1'), name='name')]
+
+ self.assertEqual(Bar.check(databases=self.databases), [
+ Error(
+ "'constraints' refers to field 'field1' which is not local to "
+ "model 'Bar'.",
+ hint='This issue may be caused by multi-table inheritance.',
+ obj=Bar,
+ id='models.E016',
+ ),
+ ])
+
+ @skipUnlessDBFeature('supports_expression_indexes')
+ def test_func_unique_constraint_pointing_to_fk(self):
+ class Foo(models.Model):
+ id = models.CharField(primary_key=True, max_length=255)
+
+ class Bar(models.Model):
+ foo_1 = models.ForeignKey(Foo, models.CASCADE, related_name='bar_1')
+ foo_2 = models.ForeignKey(Foo, models.CASCADE, related_name='bar_2')
+
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(
+ Lower('foo_1_id'),
+ Lower('foo_2'),
+ name='name',
+ ),
+ ]
+
+ self.assertEqual(Bar.check(databases=self.databases), [])