diff options
author | Hannes Ljungberg <hannes.ljungberg@gmail.com> | 2021-02-06 20:45:54 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-02-23 20:19:53 +0100 |
commit | 3aa545281e0c0f9fac93753e3769df9e0334dbaa (patch) | |
tree | 454bec922a78df9ecfe6eab197e7750e8a5bafac /tests/invalid_models_tests | |
parent | 19ce1d493ae7cbc1e704d338077b1f5f5e5769c9 (diff) | |
download | django-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.py | 141 |
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), []) |