diff options
Diffstat (limited to 'tests/invalid_models_tests/test_models.py')
| -rw-r--r-- | tests/invalid_models_tests/test_models.py | 2442 |
1 files changed, 1393 insertions, 1049 deletions
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py index c8c4cfa420..6daa14f0ad 100644 --- a/tests/invalid_models_tests/test_models.py +++ b/tests/invalid_models_tests/test_models.py @@ -17,7 +17,7 @@ def get_max_column_name_length(): allowed_len = None db_alias = None - for db in ('default', 'other'): + for db in ("default", "other"): connection = connections[db] max_name_length = connection.ops.max_name_length() if max_name_length is not None and not connection.features.truncates_names: @@ -28,60 +28,71 @@ def get_max_column_name_length(): return (allowed_len, db_alias) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class IndexTogetherTests(SimpleTestCase): - def test_non_iterable(self): class Model(models.Model): class Meta: index_together = 42 - self.assertEqual(Model.check(), [ - Error( - "'index_together' must be a list or tuple.", - obj=Model, - id='models.E008', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'index_together' must be a list or tuple.", + obj=Model, + id="models.E008", + ), + ], + ) def test_non_list(self): class Model(models.Model): class Meta: - index_together = 'not-a-list' + index_together = "not-a-list" - self.assertEqual(Model.check(), [ - Error( - "'index_together' must be a list or tuple.", - obj=Model, - id='models.E008', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'index_together' must be a list or tuple.", + obj=Model, + id="models.E008", + ), + ], + ) def test_list_containing_non_iterable(self): class Model(models.Model): class Meta: - index_together = [('a', 'b'), 42] + index_together = [("a", "b"), 42] - self.assertEqual(Model.check(), [ - Error( - "All 'index_together' elements must be lists or tuples.", - obj=Model, - id='models.E009', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "All 'index_together' elements must be lists or tuples.", + obj=Model, + id="models.E009", + ), + ], + ) def test_pointing_to_missing_field(self): class Model(models.Model): class Meta: - index_together = [['missing_field']] + index_together = [["missing_field"]] - self.assertEqual(Model.check(), [ - Error( - "'index_together' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'index_together' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_pointing_to_non_local_field(self): class Foo(models.Model): @@ -91,64 +102,76 @@ class IndexTogetherTests(SimpleTestCase): field2 = models.IntegerField() class Meta: - index_together = [['field2', 'field1']] + index_together = [["field2", "field1"]] - self.assertEqual(Bar.check(), [ - Error( - "'index_together' 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', - ), - ]) + self.assertEqual( + Bar.check(), + [ + Error( + "'index_together' 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", + ), + ], + ) def test_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - index_together = [['m2m']] + index_together = [["m2m"]] - self.assertEqual(Model.check(), [ - Error( - "'index_together' refers to a ManyToManyField 'm2m', but " - "ManyToManyFields are not permitted in 'index_together'.", - obj=Model, - id='models.E013', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'index_together' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'index_together'.", + obj=Model, + id="models.E013", + ), + ], + ) def test_pointing_to_fk(self): class Foo(models.Model): pass class Bar(models.Model): - foo_1 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_1') - foo_2 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_2') + foo_1 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_1" + ) + foo_2 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_2" + ) class Meta: - index_together = [['foo_1_id', 'foo_2']] + index_together = [["foo_1_id", "foo_2"]] self.assertEqual(Bar.check(), []) # unique_together tests are very similar to index_together tests. -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class UniqueTogetherTests(SimpleTestCase): - def test_non_iterable(self): class Model(models.Model): class Meta: unique_together = 42 - self.assertEqual(Model.check(), [ - Error( - "'unique_together' must be a list or tuple.", - obj=Model, - id='models.E010', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'unique_together' must be a list or tuple.", + obj=Model, + id="models.E010", + ), + ], + ) def test_list_containing_non_iterable(self): class Model(models.Model): @@ -156,28 +179,34 @@ class UniqueTogetherTests(SimpleTestCase): two = models.IntegerField() class Meta: - unique_together = [('a', 'b'), 42] + unique_together = [("a", "b"), 42] - self.assertEqual(Model.check(), [ - Error( - "All 'unique_together' elements must be lists or tuples.", - obj=Model, - id='models.E011', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "All 'unique_together' elements must be lists or tuples.", + obj=Model, + id="models.E011", + ), + ], + ) def test_non_list(self): class Model(models.Model): class Meta: - unique_together = 'not-a-list' + unique_together = "not-a-list" - self.assertEqual(Model.check(), [ - Error( - "'unique_together' must be a list or tuple.", - obj=Model, - id='models.E010', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'unique_together' must be a list or tuple.", + obj=Model, + id="models.E010", + ), + ], + ) def test_valid_model(self): class Model(models.Model): @@ -186,84 +215,99 @@ class UniqueTogetherTests(SimpleTestCase): class Meta: # unique_together can be a simple tuple - unique_together = ('one', 'two') + unique_together = ("one", "two") self.assertEqual(Model.check(), []) def test_pointing_to_missing_field(self): class Model(models.Model): class Meta: - unique_together = [['missing_field']] + unique_together = [["missing_field"]] - self.assertEqual(Model.check(), [ - Error( - "'unique_together' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'unique_together' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_pointing_to_m2m(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - unique_together = [['m2m']] + unique_together = [["m2m"]] - self.assertEqual(Model.check(), [ - Error( - "'unique_together' refers to a ManyToManyField 'm2m', but " - "ManyToManyFields are not permitted in 'unique_together'.", - obj=Model, - id='models.E013', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'unique_together' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'unique_together'.", + obj=Model, + id="models.E013", + ), + ], + ) def test_pointing_to_fk(self): class Foo(models.Model): pass class Bar(models.Model): - foo_1 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_1') - foo_2 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_2') + foo_1 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_1" + ) + foo_2 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_2" + ) class Meta: - unique_together = [['foo_1_id', 'foo_2']] + unique_together = [["foo_1_id", "foo_2"]] self.assertEqual(Bar.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class IndexesTests(TestCase): - def test_pointing_to_missing_field(self): class Model(models.Model): class Meta: - indexes = [models.Index(fields=['missing_field'], name='name')] + indexes = [models.Index(fields=["missing_field"], name="name")] - self.assertEqual(Model.check(), [ - Error( - "'indexes' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'indexes' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - indexes = [models.Index(fields=['m2m'], name='name')] + indexes = [models.Index(fields=["m2m"], name="name")] - self.assertEqual(Model.check(), [ - Error( - "'indexes' refers to a ManyToManyField 'm2m', but " - "ManyToManyFields are not permitted in 'indexes'.", - obj=Model, - id='models.E013', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'indexes' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'indexes'.", + obj=Model, + id="models.E013", + ), + ], + ) def test_pointing_to_non_local_field(self): class Foo(models.Model): @@ -273,28 +317,37 @@ class IndexesTests(TestCase): field2 = models.IntegerField() class Meta: - indexes = [models.Index(fields=['field2', 'field1'], name='name')] + indexes = [models.Index(fields=["field2", "field1"], name="name")] - self.assertEqual(Bar.check(), [ - Error( - "'indexes' 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', - ), - ]) + self.assertEqual( + Bar.check(), + [ + Error( + "'indexes' 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", + ), + ], + ) def test_pointing_to_fk(self): class Foo(models.Model): pass class Bar(models.Model): - foo_1 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_1') - foo_2 = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bar_2') + foo_1 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_1" + ) + foo_2 = models.ForeignKey( + Foo, on_delete=models.CASCADE, related_name="bar_2" + ) class Meta: - indexes = [models.Index(fields=['foo_1_id', 'foo_2'], name='index_name')] + indexes = [ + models.Index(fields=["foo_1_id", "foo_2"], name="index_name") + ] self.assertEqual(Bar.check(), []) @@ -302,34 +355,41 @@ class IndexesTests(TestCase): class Model(models.Model): class Meta: indexes = [ - models.Index(fields=['id'], name='_index_name'), - models.Index(fields=['id'], name='5index_name'), + models.Index(fields=["id"], name="_index_name"), + models.Index(fields=["id"], name="5index_name"), ] - self.assertEqual(Model.check(), [ - Error( - "The index name '%sindex_name' cannot start with an " - "underscore or a number." % prefix, - obj=Model, - id='models.E033', - ) for prefix in ('_', '5') - ]) + self.assertEqual( + Model.check(), + [ + Error( + "The index name '%sindex_name' cannot start with an " + "underscore or a number." % prefix, + obj=Model, + id="models.E033", + ) + for prefix in ("_", "5") + ], + ) def test_max_name_length(self): - index_name = 'x' * 31 + index_name = "x" * 31 class Model(models.Model): class Meta: - indexes = [models.Index(fields=['id'], name=index_name)] + indexes = [models.Index(fields=["id"], name=index_name)] - self.assertEqual(Model.check(), [ - Error( - "The index name '%s' cannot be longer than 30 characters." - % index_name, - obj=Model, - id='models.E034', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "The index name '%s' cannot be longer than 30 characters." + % index_name, + obj=Model, + id="models.E034", + ), + ], + ) def test_index_with_condition(self): class Model(models.Model): @@ -338,25 +398,29 @@ class IndexesTests(TestCase): class Meta: indexes = [ models.Index( - fields=['age'], - name='index_age_gte_10', + fields=["age"], + name="index_age_gte_10", condition=models.Q(age__gte=10), ), ] errors = Model.check(databases=self.databases) - expected = [] if connection.features.supports_partial_indexes else [ - Warning( - '%s does not support indexes with conditions.' - % connection.display_name, - hint=( - "Conditions will be ignored. Silence this warning if you " - "don't care about it." - ), - obj=Model, - id='models.W037', - ) - ] + expected = ( + [] + if connection.features.supports_partial_indexes + else [ + Warning( + "%s does not support indexes with conditions." + % connection.display_name, + hint=( + "Conditions will be ignored. Silence this warning if you " + "don't care about it." + ), + obj=Model, + id="models.W037", + ) + ] + ) self.assertEqual(errors, expected) def test_index_with_condition_required_db_features(self): @@ -364,11 +428,11 @@ class IndexesTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_partial_indexes'} + required_db_features = {"supports_partial_indexes"} indexes = [ models.Index( - fields=['age'], - name='index_age_gte_10', + fields=["age"], + name="index_age_gte_10", condition=models.Q(age__gte=10), ), ] @@ -382,25 +446,29 @@ class IndexesTests(TestCase): class Meta: indexes = [ models.Index( - fields=['age'], - name='index_age_include_id', - include=['id'], + fields=["age"], + name="index_age_include_id", + include=["id"], ), ] errors = Model.check(databases=self.databases) - expected = [] if connection.features.supports_covering_indexes else [ - Warning( - '%s does not support indexes with non-key columns.' - % connection.display_name, - hint=( - "Non-key columns will be ignored. Silence this warning if " - "you don't care about it." - ), - obj=Model, - id='models.W040', - ) - ] + expected = ( + [] + if connection.features.supports_covering_indexes + else [ + Warning( + "%s does not support indexes with non-key columns." + % connection.display_name, + hint=( + "Non-key columns will be ignored. Silence this warning if " + "you don't care about it." + ), + obj=Model, + id="models.W040", + ) + ] + ) self.assertEqual(errors, expected) def test_index_with_include_required_db_features(self): @@ -408,51 +476,57 @@ class IndexesTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_covering_indexes'} + required_db_features = {"supports_covering_indexes"} indexes = [ models.Index( - fields=['age'], - name='index_age_include_id', - include=['id'], + fields=["age"], + name="index_age_include_id", + include=["id"], ), ] self.assertEqual(Model.check(databases=self.databases), []) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_index_include_pointing_to_missing_field(self): class Model(models.Model): class Meta: indexes = [ - models.Index(fields=['id'], include=['missing_field'], name='name'), + models.Index(fields=["id"], include=["missing_field"], name="name"), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'indexes' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'indexes' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_index_include_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - indexes = [models.Index(fields=['id'], include=['m2m'], name='name')] + indexes = [models.Index(fields=["id"], include=["m2m"], name="name")] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'indexes' refers to a ManyToManyField 'm2m', but " - "ManyToManyFields are not permitted in 'indexes'.", - obj=Model, - id='models.E013', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'indexes' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'indexes'.", + obj=Model, + id="models.E013", + ), + ], + ) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_index_include_pointing_to_non_local_field(self): class Parent(models.Model): field1 = models.IntegerField() @@ -462,34 +536,37 @@ class IndexesTests(TestCase): class Meta: indexes = [ - models.Index(fields=['field2'], include=['field1'], name='name'), + models.Index(fields=["field2"], include=["field1"], name="name"), ] - self.assertEqual(Child.check(databases=self.databases), [ - Error( - "'indexes' refers to field 'field1' which is not local to " - "model 'Child'.", - hint='This issue may be caused by multi-table inheritance.', - obj=Child, - id='models.E016', - ), - ]) + self.assertEqual( + Child.check(databases=self.databases), + [ + Error( + "'indexes' refers to field 'field1' which is not local to " + "model 'Child'.", + hint="This issue may be caused by multi-table inheritance.", + obj=Child, + id="models.E016", + ), + ], + ) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_index_include_pointing_to_fk(self): class Target(models.Model): pass class Model(models.Model): - fk_1 = models.ForeignKey(Target, models.CASCADE, related_name='target_1') - fk_2 = models.ForeignKey(Target, models.CASCADE, related_name='target_2') + fk_1 = models.ForeignKey(Target, models.CASCADE, related_name="target_1") + fk_2 = models.ForeignKey(Target, models.CASCADE, related_name="target_2") class Meta: constraints = [ models.Index( - fields=['id'], - include=['fk_1_id', 'fk_2'], - name='name', + fields=["id"], + include=["fk_1_id", "fk_2"], + name="name", ), ] @@ -500,17 +577,16 @@ class IndexesTests(TestCase): name = models.CharField(max_length=10) class Meta: - indexes = [models.Index(Lower('name'), name='index_lower_name')] + indexes = [models.Index(Lower("name"), name="index_lower_name")] warn = Warning( - '%s does not support indexes on expressions.' - % connection.display_name, + "%s does not support indexes on expressions." % connection.display_name, hint=( "An index won't be created. Silence this warning if you don't " "care about it." ), obj=Model, - id='models.W043', + id="models.W043", ) expected = [] if connection.features.supports_expression_indexes else [warn] self.assertEqual(Model.check(databases=self.databases), expected) @@ -520,8 +596,8 @@ class IndexesTests(TestCase): name = models.CharField(max_length=10) class Meta: - indexes = [models.Index(Lower('name'), name='index_lower_name')] - required_db_features = {'supports_expression_indexes'} + indexes = [models.Index(Lower("name"), name="index_lower_name")] + required_db_features = {"supports_expression_indexes"} self.assertEqual(Model.check(databases=self.databases), []) @@ -533,8 +609,9 @@ class IndexesTests(TestCase): class Meta: indexes = [ models.Index( - models.F('height') / (models.F('weight__abs') + models.Value(5)), - name='name', + models.F("height") + / (models.F("weight__abs") + models.Value(5)), + name="name", ), ] @@ -544,46 +621,55 @@ class IndexesTests(TestCase): def test_func_index_pointing_to_missing_field(self): class Model(models.Model): class Meta: - indexes = [models.Index(Lower('missing_field').desc(), name='name')] + indexes = [models.Index(Lower("missing_field").desc(), name="name")] - self.assertEqual(Model.check(), [ - Error( - "'indexes' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'indexes' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_func_index_pointing_to_missing_field_nested(self): class Model(models.Model): class Meta: indexes = [ - models.Index(Abs(Round('missing_field')), name='name'), + models.Index(Abs(Round("missing_field")), name="name"), ] - self.assertEqual(Model.check(), [ - Error( - "'indexes' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'indexes' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_func_index_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - indexes = [models.Index(Lower('m2m'), name='name')] + indexes = [models.Index(Lower("m2m"), name="name")] - self.assertEqual(Model.check(), [ - Error( - "'indexes' refers to a ManyToManyField 'm2m', but " - "ManyToManyFields are not permitted in 'indexes'.", - obj=Model, - id='models.E013', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'indexes' refers to a ManyToManyField 'm2m', but " + "ManyToManyFields are not permitted in 'indexes'.", + obj=Model, + id="models.E013", + ), + ], + ) def test_func_index_pointing_to_non_local_field(self): class Foo(models.Model): @@ -591,87 +677,99 @@ class IndexesTests(TestCase): class Bar(Foo): class Meta: - indexes = [models.Index(Lower('field1'), name='name')] + indexes = [models.Index(Lower("field1"), name="name")] - self.assertEqual(Bar.check(), [ - Error( - "'indexes' 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', - ), - ]) + self.assertEqual( + Bar.check(), + [ + Error( + "'indexes' 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", + ), + ], + ) def test_func_index_pointing_to_fk(self): class Foo(models.Model): pass 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') + foo_1 = models.ForeignKey(Foo, models.CASCADE, related_name="bar_1") + foo_2 = models.ForeignKey(Foo, models.CASCADE, related_name="bar_2") class Meta: indexes = [ - models.Index(Lower('foo_1_id'), Lower('foo_2'), name='index_name'), + models.Index(Lower("foo_1_id"), Lower("foo_2"), name="index_name"), ] self.assertEqual(Bar.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class FieldNamesTests(TestCase): - databases = {'default', 'other'} + databases = {"default", "other"} def test_ending_with_underscore(self): class Model(models.Model): field_ = models.CharField(max_length=10) - m2m_ = models.ManyToManyField('self') + m2m_ = models.ManyToManyField("self") - self.assertEqual(Model.check(), [ - Error( - 'Field names must not end with an underscore.', - obj=Model._meta.get_field('field_'), - id='fields.E001', - ), - Error( - 'Field names must not end with an underscore.', - obj=Model._meta.get_field('m2m_'), - id='fields.E001', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "Field names must not end with an underscore.", + obj=Model._meta.get_field("field_"), + id="fields.E001", + ), + Error( + "Field names must not end with an underscore.", + obj=Model._meta.get_field("m2m_"), + id="fields.E001", + ), + ], + ) max_column_name_length, column_limit_db_alias = get_max_column_name_length() - @unittest.skipIf(max_column_name_length is None, "The database doesn't have a column name length limit.") + @unittest.skipIf( + max_column_name_length is None, + "The database doesn't have a column name length limit.", + ) def test_M2M_long_column_name(self): """ #13711 -- Model check for long M2M column names when database has column name length limits. """ # A model with very long name which will be used to set relations to. - class VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz(models.Model): + class VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz( + models.Model + ): title = models.CharField(max_length=11) # Main model for which checks will be performed. class ModelWithLongField(models.Model): m2m_field = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, - related_name='rn1', + related_name="rn1", ) m2m_field2 = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, - related_name='rn2', through='m2msimple', + related_name="rn2", + through="m2msimple", ) m2m_field3 = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, - related_name='rn3', - through='m2mcomplex', + related_name="rn3", + through="m2mcomplex", ) fk = models.ForeignKey( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, models.CASCADE, - related_name='rn4', + related_name="rn4", ) # Models used for setting `through` in M2M field. @@ -681,7 +779,7 @@ class FieldNamesTests(TestCase): class m2mcomplex(models.Model): id2 = models.ForeignKey(ModelWithLongField, models.CASCADE) - long_field_name = 'a' * (self.max_column_name_length + 1) + long_field_name = "a" * (self.max_column_name_length + 1) models.ForeignKey( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, models.CASCADE, @@ -690,13 +788,15 @@ class FieldNamesTests(TestCase): models.ForeignKey( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, models.CASCADE, - db_column=long_field_name + db_column=long_field_name, ).contribute_to_class(m2mcomplex, long_field_name) - errors = ModelWithLongField.check(databases=('default', 'other')) + errors = ModelWithLongField.check(databases=("default", "other")) # First error because of M2M field set on the model with long name. - m2m_long_name = "verylongmodelnamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz_id" + m2m_long_name = ( + "verylongmodelnamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz_id" + ) if self.max_column_name_length > len(m2m_long_name): # Some databases support names longer than the test name. expected = [] @@ -705,11 +805,15 @@ class FieldNamesTests(TestCase): Error( 'Autogenerated column name too long for M2M field "%s". ' 'Maximum length is "%s" for database "%s".' - % (m2m_long_name, self.max_column_name_length, self.column_limit_db_alias), + % ( + m2m_long_name, + self.max_column_name_length, + self.column_limit_db_alias, + ), hint="Use 'through' to create a separate model for " - "M2M and then set column_name using 'db_column'.", + "M2M and then set column_name using 'db_column'.", obj=ModelWithLongField, - id='models.E019', + id="models.E019", ) ] @@ -722,11 +826,15 @@ class FieldNamesTests(TestCase): Error( 'Autogenerated column name too long for M2M field "%s_id". ' 'Maximum length is "%s" for database "%s".' - % (long_field_name, self.max_column_name_length, self.column_limit_db_alias), + % ( + long_field_name, + self.max_column_name_length, + self.column_limit_db_alias, + ), hint="Use 'through' to create a separate model for " - "M2M and then set column_name using 'db_column'.", + "M2M and then set column_name using 'db_column'.", obj=ModelWithLongField, - id='models.E019', + id="models.E019", ) ) @@ -735,29 +843,44 @@ class FieldNamesTests(TestCase): # aliases. self.assertEqual(ModelWithLongField.check(databases=None), []) - @unittest.skipIf(max_column_name_length is None, "The database doesn't have a column name length limit.") + @unittest.skipIf( + max_column_name_length is None, + "The database doesn't have a column name length limit.", + ) def test_local_field_long_column_name(self): """ #13711 -- Model check for long column names when database does not support long names. """ + class ModelWithLongField(models.Model): title = models.CharField(max_length=11) - long_field_name = 'a' * (self.max_column_name_length + 1) - long_field_name2 = 'b' * (self.max_column_name_length + 1) - models.CharField(max_length=11).contribute_to_class(ModelWithLongField, long_field_name) - models.CharField(max_length=11, db_column='vlmn').contribute_to_class(ModelWithLongField, long_field_name2) - self.assertEqual(ModelWithLongField.check(databases=('default', 'other')), [ - Error( - 'Autogenerated column name too long for field "%s". ' - 'Maximum length is "%s" for database "%s".' - % (long_field_name, self.max_column_name_length, self.column_limit_db_alias), - hint="Set the column name manually using 'db_column'.", - obj=ModelWithLongField, - id='models.E018', - ) - ]) + long_field_name = "a" * (self.max_column_name_length + 1) + long_field_name2 = "b" * (self.max_column_name_length + 1) + models.CharField(max_length=11).contribute_to_class( + ModelWithLongField, long_field_name + ) + models.CharField(max_length=11, db_column="vlmn").contribute_to_class( + ModelWithLongField, long_field_name2 + ) + self.assertEqual( + ModelWithLongField.check(databases=("default", "other")), + [ + Error( + 'Autogenerated column name too long for field "%s". ' + 'Maximum length is "%s" for database "%s".' + % ( + long_field_name, + self.max_column_name_length, + self.column_limit_db_alias, + ), + hint="Set the column name manually using 'db_column'.", + obj=ModelWithLongField, + id="models.E018", + ) + ], + ) # Check for long column names is called only for specified database # aliases. self.assertEqual(ModelWithLongField.check(databases=None), []) @@ -766,45 +889,53 @@ class FieldNamesTests(TestCase): class Model(models.Model): some__field = models.IntegerField() - self.assertEqual(Model.check(), [ - Error( - 'Field names must not contain "__".', - obj=Model._meta.get_field('some__field'), - id='fields.E002', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + 'Field names must not contain "__".', + obj=Model._meta.get_field("some__field"), + id="fields.E002", + ) + ], + ) def test_pk(self): class Model(models.Model): pk = models.IntegerField() - self.assertEqual(Model.check(), [ - Error( - "'pk' is a reserved word that cannot be used as a field name.", - obj=Model._meta.get_field('pk'), - id='fields.E003', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'pk' is a reserved word that cannot be used as a field name.", + obj=Model._meta.get_field("pk"), + id="fields.E003", + ) + ], + ) def test_db_column_clash(self): class Model(models.Model): foo = models.IntegerField() - bar = models.IntegerField(db_column='foo') + bar = models.IntegerField(db_column="foo") - self.assertEqual(Model.check(), [ - Error( - "Field 'bar' has column name 'foo' that is used by " - "another field.", - hint="Specify a 'db_column' for the field.", - obj=Model, - id='models.E007', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "Field 'bar' has column name 'foo' that is used by " + "another field.", + hint="Specify a 'db_column' for the field.", + obj=Model, + id="models.E007", + ) + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ShadowingFieldsTests(SimpleTestCase): - def test_field_name_clash_with_child_accessor(self): class Parent(models.Model): pass @@ -812,41 +943,47 @@ class ShadowingFieldsTests(SimpleTestCase): class Child(Parent): child = models.CharField(max_length=100) - self.assertEqual(Child.check(), [ - Error( - "The field 'child' clashes with the field " - "'child' from model 'invalid_models_tests.parent'.", - obj=Child._meta.get_field('child'), - id='models.E006', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "The field 'child' clashes with the field " + "'child' from model 'invalid_models_tests.parent'.", + obj=Child._meta.get_field("child"), + id="models.E006", + ) + ], + ) def test_field_name_clash_with_m2m_through(self): class Parent(models.Model): clash_id = models.IntegerField() class Child(Parent): - clash = models.ForeignKey('Child', models.CASCADE) + clash = models.ForeignKey("Child", models.CASCADE) class Model(models.Model): parents = models.ManyToManyField( to=Parent, - through='Through', - through_fields=['parent', 'model'], + through="Through", + through_fields=["parent", "model"], ) class Through(models.Model): parent = models.ForeignKey(Parent, models.CASCADE) model = models.ForeignKey(Model, models.CASCADE) - self.assertEqual(Child.check(), [ - Error( - "The field 'clash' clashes with the field 'clash_id' from " - "model 'invalid_models_tests.parent'.", - obj=Child._meta.get_field('clash'), - id='models.E006', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "The field 'clash' clashes with the field 'clash_id' from " + "model 'invalid_models_tests.parent'.", + obj=Child._meta.get_field("clash"), + id="models.E006", + ) + ], + ) def test_multiinheritance_clash(self): class Mother(models.Model): @@ -860,22 +997,25 @@ class ShadowingFieldsTests(SimpleTestCase): # both parents define these fields. pass - self.assertEqual(Child.check(), [ - Error( - "The field 'id' from parent model " - "'invalid_models_tests.mother' clashes with the field 'id' " - "from parent model 'invalid_models_tests.father'.", - obj=Child, - id='models.E005', - ), - Error( - "The field 'clash' from parent model " - "'invalid_models_tests.mother' clashes with the field 'clash' " - "from parent model 'invalid_models_tests.father'.", - obj=Child, - id='models.E005', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "The field 'id' from parent model " + "'invalid_models_tests.mother' clashes with the field 'id' " + "from parent model 'invalid_models_tests.father'.", + obj=Child, + id="models.E005", + ), + Error( + "The field 'clash' from parent model " + "'invalid_models_tests.mother' clashes with the field 'clash' " + "from parent model 'invalid_models_tests.father'.", + obj=Child, + id="models.E005", + ), + ], + ) def test_inheritance_clash(self): class Parent(models.Model): @@ -889,14 +1029,17 @@ class ShadowingFieldsTests(SimpleTestCase): # This field clashes with parent "f_id" field. f = models.ForeignKey(Target, models.CASCADE) - self.assertEqual(Child.check(), [ - Error( - "The field 'f' clashes with the field 'f_id' " - "from model 'invalid_models_tests.parent'.", - obj=Child._meta.get_field('f'), - id='models.E006', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "The field 'f' clashes with the field 'f_id' " + "from model 'invalid_models_tests.parent'.", + obj=Child._meta.get_field("f"), + id="models.E006", + ) + ], + ) def test_multigeneration_inheritance(self): class GrandParent(models.Model): @@ -911,14 +1054,17 @@ class ShadowingFieldsTests(SimpleTestCase): class GrandChild(Child): clash = models.IntegerField() - self.assertEqual(GrandChild.check(), [ - Error( - "The field 'clash' clashes with the field 'clash' " - "from model 'invalid_models_tests.grandparent'.", - obj=GrandChild._meta.get_field('clash'), - id='models.E006', - ) - ]) + self.assertEqual( + GrandChild.check(), + [ + Error( + "The field 'clash' clashes with the field 'clash' " + "from model 'invalid_models_tests.grandparent'.", + obj=GrandChild._meta.get_field("clash"), + id="models.E006", + ) + ], + ) def test_id_clash(self): class Target(models.Model): @@ -928,54 +1074,62 @@ class ShadowingFieldsTests(SimpleTestCase): fk = models.ForeignKey(Target, models.CASCADE) fk_id = models.IntegerField() - self.assertEqual(Model.check(), [ - Error( - "The field 'fk_id' clashes with the field 'fk' from model " - "'invalid_models_tests.model'.", - obj=Model._meta.get_field('fk_id'), - id='models.E006', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "The field 'fk_id' clashes with the field 'fk' from model " + "'invalid_models_tests.model'.", + obj=Model._meta.get_field("fk_id"), + id="models.E006", + ) + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class OtherModelTests(SimpleTestCase): - def test_unique_primary_key(self): invalid_id = models.IntegerField(primary_key=False) class Model(models.Model): id = invalid_id - self.assertEqual(Model.check(), [ - Error( - "'id' can only be used as a field name if the field also sets " - "'primary_key=True'.", - obj=Model, - id='models.E004', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'id' can only be used as a field name if the field also sets " + "'primary_key=True'.", + obj=Model, + id="models.E004", + ), + ], + ) def test_ordering_non_iterable(self): class Model(models.Model): class Meta: - ordering = 'missing_field' + ordering = "missing_field" - self.assertEqual(Model.check(), [ - Error( - "'ordering' must be a tuple or list " - "(even if you want to order by only one field).", - obj=Model, - id='models.E014', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'ordering' must be a tuple or list " + "(even if you want to order by only one field).", + obj=Model, + id="models.E014", + ), + ], + ) def test_just_ordering_no_errors(self): class Model(models.Model): order = models.PositiveIntegerField() class Meta: - ordering = ['order'] + ordering = ["order"] self.assertEqual(Model.check(), []) @@ -987,7 +1141,7 @@ class OtherModelTests(SimpleTestCase): question = models.ForeignKey(Question, models.CASCADE) class Meta: - order_with_respect_to = 'question' + order_with_respect_to = "question" self.assertEqual(Answer.check(), []) @@ -1000,16 +1154,19 @@ class OtherModelTests(SimpleTestCase): order = models.IntegerField() class Meta: - order_with_respect_to = 'question' - ordering = ['order'] + order_with_respect_to = "question" + ordering = ["order"] - self.assertEqual(Answer.check(), [ - Error( - "'ordering' and 'order_with_respect_to' cannot be used together.", - obj=Answer, - id='models.E021', - ), - ]) + self.assertEqual( + Answer.check(), + [ + Error( + "'ordering' and 'order_with_respect_to' cannot be used together.", + obj=Answer, + id="models.E021", + ), + ], + ) def test_non_valid(self): class RelationModel(models.Model): @@ -1019,62 +1176,74 @@ class OtherModelTests(SimpleTestCase): relation = models.ManyToManyField(RelationModel) class Meta: - ordering = ['relation'] + ordering = ["relation"] - self.assertEqual(Model.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'relation'.", - obj=Model, - id='models.E015', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'relation'.", + obj=Model, + id="models.E015", + ), + ], + ) def test_ordering_pointing_to_missing_field(self): class Model(models.Model): class Meta: - ordering = ('missing_field',) + ordering = ("missing_field",) - self.assertEqual(Model.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'missing_field'.", - obj=Model, - id='models.E015', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'missing_field'.", + obj=Model, + id="models.E015", + ) + ], + ) def test_ordering_pointing_to_missing_foreignkey_field(self): class Model(models.Model): missing_fk_field = models.IntegerField() class Meta: - ordering = ('missing_fk_field_id',) + ordering = ("missing_fk_field_id",) - self.assertEqual(Model.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'missing_fk_field_id'.", - obj=Model, - id='models.E015', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'missing_fk_field_id'.", + obj=Model, + id="models.E015", + ) + ], + ) def test_ordering_pointing_to_missing_related_field(self): class Model(models.Model): test = models.IntegerField() class Meta: - ordering = ('missing_related__id',) + ordering = ("missing_related__id",) - self.assertEqual(Model.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'missing_related__id'.", - obj=Model, - id='models.E015', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'missing_related__id'.", + obj=Model, + id="models.E015", + ) + ], + ) def test_ordering_pointing_to_missing_related_model_field(self): class Parent(models.Model): @@ -1084,32 +1253,38 @@ class OtherModelTests(SimpleTestCase): parent = models.ForeignKey(Parent, models.CASCADE) class Meta: - ordering = ('parent__missing_field',) + ordering = ("parent__missing_field",) - self.assertEqual(Child.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'parent__missing_field'.", - obj=Child, - id='models.E015', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'parent__missing_field'.", + obj=Child, + id="models.E015", + ) + ], + ) def test_ordering_pointing_to_non_related_field(self): class Child(models.Model): parent = models.IntegerField() class Meta: - ordering = ('parent__missing_field',) + ordering = ("parent__missing_field",) - self.assertEqual(Child.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'parent__missing_field'.", - obj=Child, - id='models.E015', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'parent__missing_field'.", + obj=Child, + id="models.E015", + ) + ], + ) def test_ordering_pointing_to_two_related_model_field(self): class Parent2(models.Model): @@ -1122,16 +1297,19 @@ class OtherModelTests(SimpleTestCase): parent1 = models.ForeignKey(Parent1, models.CASCADE) class Meta: - ordering = ('parent1__parent2__missing_field',) + ordering = ("parent1__parent2__missing_field",) - self.assertEqual(Child.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'parent1__parent2__missing_field'.", - obj=Child, - id='models.E015', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'parent1__parent2__missing_field'.", + obj=Child, + id="models.E015", + ) + ], + ) def test_ordering_pointing_multiple_times_to_model_fields(self): class Parent(models.Model): @@ -1142,23 +1320,26 @@ class OtherModelTests(SimpleTestCase): parent = models.ForeignKey(Parent, models.CASCADE) class Meta: - ordering = ('parent__field1__field2',) + ordering = ("parent__field1__field2",) - self.assertEqual(Child.check(), [ - Error( - "'ordering' refers to the nonexistent field, related field, " - "or lookup 'parent__field1__field2'.", - obj=Child, - id='models.E015', - ) - ]) + self.assertEqual( + Child.check(), + [ + Error( + "'ordering' refers to the nonexistent field, related field, " + "or lookup 'parent__field1__field2'.", + obj=Child, + id="models.E015", + ) + ], + ) def test_ordering_allows_registered_lookups(self): class Model(models.Model): test = models.CharField(max_length=100) class Meta: - ordering = ('test__lower',) + ordering = ("test__lower",) with register_lookup(models.CharField, Lower): self.assertEqual(Model.check(), []) @@ -1168,7 +1349,7 @@ class OtherModelTests(SimpleTestCase): test = models.CharField(max_length=100) class Meta: - ordering = ('test__isnull',) + ordering = ("test__isnull",) self.assertEqual(Model.check(), []) @@ -1180,7 +1361,7 @@ class OtherModelTests(SimpleTestCase): parent = models.ForeignKey(Parent, models.CASCADE) class Meta: - ordering = ('parent__pk',) + ordering = ("parent__pk",) self.assertEqual(Child.check(), []) @@ -1192,7 +1373,7 @@ class OtherModelTests(SimpleTestCase): parent = models.ForeignKey(Parent, models.CASCADE) class Meta: - ordering = ('parent_id',) + ordering = ("parent_id",) self.assertFalse(Child.check()) @@ -1200,116 +1381,144 @@ class OtherModelTests(SimpleTestCase): class _Model(models.Model): pass - self.assertEqual(_Model.check(), [ - Error( - "The model name '_Model' cannot start or end with an underscore " - "as it collides with the query lookup syntax.", - obj=_Model, - id='models.E023', - ) - ]) + self.assertEqual( + _Model.check(), + [ + Error( + "The model name '_Model' cannot start or end with an underscore " + "as it collides with the query lookup syntax.", + obj=_Model, + id="models.E023", + ) + ], + ) def test_name_ending_with_underscore(self): class Model_(models.Model): pass - self.assertEqual(Model_.check(), [ - Error( - "The model name 'Model_' cannot start or end with an underscore " - "as it collides with the query lookup syntax.", - obj=Model_, - id='models.E023', - ) - ]) + self.assertEqual( + Model_.check(), + [ + Error( + "The model name 'Model_' cannot start or end with an underscore " + "as it collides with the query lookup syntax.", + obj=Model_, + id="models.E023", + ) + ], + ) def test_name_contains_double_underscores(self): class Test__Model(models.Model): pass - self.assertEqual(Test__Model.check(), [ - Error( - "The model name 'Test__Model' cannot contain double underscores " - "as it collides with the query lookup syntax.", - obj=Test__Model, - id='models.E024', - ) - ]) + self.assertEqual( + Test__Model.check(), + [ + Error( + "The model name 'Test__Model' cannot contain double underscores " + "as it collides with the query lookup syntax.", + obj=Test__Model, + id="models.E024", + ) + ], + ) def test_property_and_related_field_accessor_clash(self): class Model(models.Model): - fk = models.ForeignKey('self', models.CASCADE) + fk = models.ForeignKey("self", models.CASCADE) # Override related field accessor. - Model.fk_id = property(lambda self: 'ERROR') + Model.fk_id = property(lambda self: "ERROR") - self.assertEqual(Model.check(), [ - Error( - "The property 'fk_id' clashes with a related field accessor.", - obj=Model, - id='models.E025', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "The property 'fk_id' clashes with a related field accessor.", + obj=Model, + id="models.E025", + ) + ], + ) def test_single_primary_key(self): class Model(models.Model): foo = models.IntegerField(primary_key=True) bar = models.IntegerField(primary_key=True) - self.assertEqual(Model.check(), [ - Error( - "The model cannot have more than one field with 'primary_key=True'.", - obj=Model, - id='models.E026', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "The model cannot have more than one field with 'primary_key=True'.", + obj=Model, + id="models.E026", + ) + ], + ) - @override_settings(TEST_SWAPPED_MODEL_BAD_VALUE='not-a-model') + @override_settings(TEST_SWAPPED_MODEL_BAD_VALUE="not-a-model") def test_swappable_missing_app_name(self): class Model(models.Model): class Meta: - swappable = 'TEST_SWAPPED_MODEL_BAD_VALUE' + swappable = "TEST_SWAPPED_MODEL_BAD_VALUE" - self.assertEqual(Model.check(), [ - Error( - "'TEST_SWAPPED_MODEL_BAD_VALUE' is not of the form 'app_label.app_name'.", - id='models.E001', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'TEST_SWAPPED_MODEL_BAD_VALUE' is not of the form 'app_label.app_name'.", + id="models.E001", + ), + ], + ) - @override_settings(TEST_SWAPPED_MODEL_BAD_MODEL='not_an_app.Target') + @override_settings(TEST_SWAPPED_MODEL_BAD_MODEL="not_an_app.Target") def test_swappable_missing_app(self): class Model(models.Model): class Meta: - swappable = 'TEST_SWAPPED_MODEL_BAD_MODEL' + swappable = "TEST_SWAPPED_MODEL_BAD_MODEL" - self.assertEqual(Model.check(), [ - Error( - "'TEST_SWAPPED_MODEL_BAD_MODEL' references 'not_an_app.Target', " - 'which has not been installed, or is abstract.', - id='models.E002', - ), - ]) + self.assertEqual( + Model.check(), + [ + Error( + "'TEST_SWAPPED_MODEL_BAD_MODEL' references 'not_an_app.Target', " + "which has not been installed, or is abstract.", + id="models.E002", + ), + ], + ) def test_two_m2m_through_same_relationship(self): class Person(models.Model): pass class Group(models.Model): - primary = models.ManyToManyField(Person, through='Membership', related_name='primary') - secondary = models.ManyToManyField(Person, through='Membership', related_name='secondary') + primary = models.ManyToManyField( + Person, through="Membership", related_name="primary" + ) + secondary = models.ManyToManyField( + Person, through="Membership", related_name="secondary" + ) class Membership(models.Model): person = models.ForeignKey(Person, models.CASCADE) group = models.ForeignKey(Group, models.CASCADE) - self.assertEqual(Group.check(), [ - Error( - "The model has two identical many-to-many relations through " - "the intermediate model 'invalid_models_tests.Membership'.", - obj=Group, - id='models.E003', - ) - ]) + self.assertEqual( + Group.check(), + [ + Error( + "The model has two identical many-to-many relations through " + "the intermediate model 'invalid_models_tests.Membership'.", + obj=Group, + id="models.E003", + ) + ], + ) def test_two_m2m_through_same_model_with_different_through_fields(self): class Country(models.Model): @@ -1317,13 +1526,15 @@ class OtherModelTests(SimpleTestCase): class ShippingMethod(models.Model): to_countries = models.ManyToManyField( - Country, through='ShippingMethodPrice', - through_fields=('method', 'to_country'), + Country, + through="ShippingMethodPrice", + through_fields=("method", "to_country"), ) from_countries = models.ManyToManyField( - Country, through='ShippingMethodPrice', - through_fields=('method', 'from_country'), - related_name='+', + Country, + through="ShippingMethodPrice", + through_fields=("method", "from_country"), + related_name="+", ) class ShippingMethodPrice(models.Model): @@ -1338,7 +1549,9 @@ class OtherModelTests(SimpleTestCase): pass class ParkingLot(Place): - other_place = models.OneToOneField(Place, models.CASCADE, related_name='other_parking') + other_place = models.OneToOneField( + Place, models.CASCADE, related_name="other_parking" + ) self.assertEqual(ParkingLot.check(), []) @@ -1347,162 +1560,190 @@ class OtherModelTests(SimpleTestCase): pass class ParkingLot(Place): - place = models.OneToOneField(Place, models.CASCADE, parent_link=True, primary_key=True) - other_place = models.OneToOneField(Place, models.CASCADE, related_name='other_parking') + place = models.OneToOneField( + Place, models.CASCADE, parent_link=True, primary_key=True + ) + other_place = models.OneToOneField( + Place, models.CASCADE, related_name="other_parking" + ) self.assertEqual(ParkingLot.check(), []) def test_m2m_table_name_clash(self): class Foo(models.Model): - bar = models.ManyToManyField('Bar', db_table='myapp_bar') + bar = models.ManyToManyField("Bar", db_table="myapp_bar") class Meta: - db_table = 'myapp_foo' + db_table = "myapp_foo" class Bar(models.Model): class Meta: - db_table = 'myapp_bar' + db_table = "myapp_bar" - self.assertEqual(Foo.check(), [ - Error( - "The field's intermediary table 'myapp_bar' clashes with the " - "table name of 'invalid_models_tests.Bar'.", - obj=Foo._meta.get_field('bar'), - id='fields.E340', - ) - ]) + self.assertEqual( + Foo.check(), + [ + Error( + "The field's intermediary table 'myapp_bar' clashes with the " + "table name of 'invalid_models_tests.Bar'.", + obj=Foo._meta.get_field("bar"), + id="fields.E340", + ) + ], + ) - @override_settings(DATABASE_ROUTERS=['invalid_models_tests.test_models.EmptyRouter']) + @override_settings( + DATABASE_ROUTERS=["invalid_models_tests.test_models.EmptyRouter"] + ) def test_m2m_table_name_clash_database_routers_installed(self): class Foo(models.Model): - bar = models.ManyToManyField('Bar', db_table='myapp_bar') + bar = models.ManyToManyField("Bar", db_table="myapp_bar") class Meta: - db_table = 'myapp_foo' + db_table = "myapp_foo" class Bar(models.Model): class Meta: - db_table = 'myapp_bar' + db_table = "myapp_bar" - self.assertEqual(Foo.check(), [ - Warning( - "The field's intermediary table 'myapp_bar' clashes with the " - "table name of 'invalid_models_tests.Bar'.", - obj=Foo._meta.get_field('bar'), - hint=( - "You have configured settings.DATABASE_ROUTERS. Verify " - "that the table of 'invalid_models_tests.Bar' is " - "correctly routed to a separate database." + self.assertEqual( + Foo.check(), + [ + Warning( + "The field's intermediary table 'myapp_bar' clashes with the " + "table name of 'invalid_models_tests.Bar'.", + obj=Foo._meta.get_field("bar"), + hint=( + "You have configured settings.DATABASE_ROUTERS. Verify " + "that the table of 'invalid_models_tests.Bar' is " + "correctly routed to a separate database." + ), + id="fields.W344", ), - id='fields.W344', - ), - ]) + ], + ) def test_m2m_field_table_name_clash(self): class Foo(models.Model): pass class Bar(models.Model): - foos = models.ManyToManyField(Foo, db_table='clash') + foos = models.ManyToManyField(Foo, db_table="clash") class Baz(models.Model): - foos = models.ManyToManyField(Foo, db_table='clash') + foos = models.ManyToManyField(Foo, db_table="clash") - self.assertEqual(Bar.check() + Baz.check(), [ - Error( - "The field's intermediary table 'clash' clashes with the " - "table name of 'invalid_models_tests.Baz.foos'.", - obj=Bar._meta.get_field('foos'), - id='fields.E340', - ), - Error( - "The field's intermediary table 'clash' clashes with the " - "table name of 'invalid_models_tests.Bar.foos'.", - obj=Baz._meta.get_field('foos'), - id='fields.E340', - ) - ]) + self.assertEqual( + Bar.check() + Baz.check(), + [ + Error( + "The field's intermediary table 'clash' clashes with the " + "table name of 'invalid_models_tests.Baz.foos'.", + obj=Bar._meta.get_field("foos"), + id="fields.E340", + ), + Error( + "The field's intermediary table 'clash' clashes with the " + "table name of 'invalid_models_tests.Bar.foos'.", + obj=Baz._meta.get_field("foos"), + id="fields.E340", + ), + ], + ) - @override_settings(DATABASE_ROUTERS=['invalid_models_tests.test_models.EmptyRouter']) + @override_settings( + DATABASE_ROUTERS=["invalid_models_tests.test_models.EmptyRouter"] + ) def test_m2m_field_table_name_clash_database_routers_installed(self): class Foo(models.Model): pass class Bar(models.Model): - foos = models.ManyToManyField(Foo, db_table='clash') + foos = models.ManyToManyField(Foo, db_table="clash") class Baz(models.Model): - foos = models.ManyToManyField(Foo, db_table='clash') + foos = models.ManyToManyField(Foo, db_table="clash") - self.assertEqual(Bar.check() + Baz.check(), [ - Warning( - "The field's intermediary table 'clash' clashes with the " - "table name of 'invalid_models_tests.%s.foos'." - % clashing_model, - obj=model_cls._meta.get_field('foos'), - hint=( - "You have configured settings.DATABASE_ROUTERS. Verify " - "that the table of 'invalid_models_tests.%s.foos' is " - "correctly routed to a separate database." % clashing_model - ), - id='fields.W344', - ) for model_cls, clashing_model in [(Bar, 'Baz'), (Baz, 'Bar')] - ]) + self.assertEqual( + Bar.check() + Baz.check(), + [ + Warning( + "The field's intermediary table 'clash' clashes with the " + "table name of 'invalid_models_tests.%s.foos'." % clashing_model, + obj=model_cls._meta.get_field("foos"), + hint=( + "You have configured settings.DATABASE_ROUTERS. Verify " + "that the table of 'invalid_models_tests.%s.foos' is " + "correctly routed to a separate database." % clashing_model + ), + id="fields.W344", + ) + for model_cls, clashing_model in [(Bar, "Baz"), (Baz, "Bar")] + ], + ) def test_m2m_autogenerated_table_name_clash(self): class Foo(models.Model): class Meta: - db_table = 'bar_foos' + db_table = "bar_foos" class Bar(models.Model): # The autogenerated `db_table` will be bar_foos. foos = models.ManyToManyField(Foo) class Meta: - db_table = 'bar' + db_table = "bar" - self.assertEqual(Bar.check(), [ - Error( - "The field's intermediary table 'bar_foos' clashes with the " - "table name of 'invalid_models_tests.Foo'.", - obj=Bar._meta.get_field('foos'), - id='fields.E340', - ) - ]) + self.assertEqual( + Bar.check(), + [ + Error( + "The field's intermediary table 'bar_foos' clashes with the " + "table name of 'invalid_models_tests.Foo'.", + obj=Bar._meta.get_field("foos"), + id="fields.E340", + ) + ], + ) - @override_settings(DATABASE_ROUTERS=['invalid_models_tests.test_models.EmptyRouter']) + @override_settings( + DATABASE_ROUTERS=["invalid_models_tests.test_models.EmptyRouter"] + ) def test_m2m_autogenerated_table_name_clash_database_routers_installed(self): class Foo(models.Model): class Meta: - db_table = 'bar_foos' + db_table = "bar_foos" class Bar(models.Model): # The autogenerated db_table is bar_foos. foos = models.ManyToManyField(Foo) class Meta: - db_table = 'bar' + db_table = "bar" - self.assertEqual(Bar.check(), [ - Warning( - "The field's intermediary table 'bar_foos' clashes with the " - "table name of 'invalid_models_tests.Foo'.", - obj=Bar._meta.get_field('foos'), - hint=( - "You have configured settings.DATABASE_ROUTERS. Verify " - "that the table of 'invalid_models_tests.Foo' is " - "correctly routed to a separate database." + self.assertEqual( + Bar.check(), + [ + Warning( + "The field's intermediary table 'bar_foos' clashes with the " + "table name of 'invalid_models_tests.Foo'.", + obj=Bar._meta.get_field("foos"), + hint=( + "You have configured settings.DATABASE_ROUTERS. Verify " + "that the table of 'invalid_models_tests.Foo' is " + "correctly routed to a separate database." + ), + id="fields.W344", ), - id='fields.W344', - ), - ]) + ], + ) def test_m2m_unmanaged_shadow_models_not_checked(self): class A1(models.Model): pass class C1(models.Model): - mm_a = models.ManyToManyField(A1, db_table='d1') + mm_a = models.ManyToManyField(A1, db_table="d1") # Unmanaged models that shadow the above models. Reused table names # shouldn't be flagged by any checks. @@ -1511,17 +1752,17 @@ class OtherModelTests(SimpleTestCase): managed = False class C2(models.Model): - mm_a = models.ManyToManyField(A2, through='Intermediate') + mm_a = models.ManyToManyField(A2, through="Intermediate") class Meta: managed = False class Intermediate(models.Model): - a2 = models.ForeignKey(A2, models.CASCADE, db_column='a1_id') - c2 = models.ForeignKey(C2, models.CASCADE, db_column='c1_id') + a2 = models.ForeignKey(A2, models.CASCADE, db_column="a1_id") + c2 = models.ForeignKey(C2, models.CASCADE, db_column="c1_id") class Meta: - db_table = 'd1' + db_table = "d1" managed = False self.assertEqual(C1.check(), []) @@ -1532,8 +1773,8 @@ class OtherModelTests(SimpleTestCase): pass class Through(models.Model): - a = models.ForeignKey('A', models.CASCADE) - c = models.ForeignKey('C', models.CASCADE) + a = models.ForeignKey("A", models.CASCADE) + c = models.ForeignKey("C", models.CASCADE) class ThroughProxy(Through): class Meta: @@ -1541,17 +1782,19 @@ class OtherModelTests(SimpleTestCase): class C(models.Model): mm_a = models.ManyToManyField(A, through=Through) - mm_aproxy = models.ManyToManyField(A, through=ThroughProxy, related_name='proxied_m2m') + mm_aproxy = models.ManyToManyField( + A, through=ThroughProxy, related_name="proxied_m2m" + ) self.assertEqual(C.check(), []) - @isolate_apps('django.contrib.auth', kwarg_name='apps') + @isolate_apps("django.contrib.auth", kwarg_name="apps") def test_lazy_reference_checks(self, apps): class DummyModel(models.Model): - author = models.ForeignKey('Author', models.CASCADE) + author = models.ForeignKey("Author", models.CASCADE) class Meta: - app_label = 'invalid_models_tests' + app_label = "invalid_models_tests" class DummyClass: def __call__(self, **kwargs): @@ -1563,59 +1806,65 @@ class OtherModelTests(SimpleTestCase): def dummy_function(*args, **kwargs): pass - apps.lazy_model_operation(dummy_function, ('auth', 'imaginarymodel')) - apps.lazy_model_operation(dummy_function, ('fanciful_app', 'imaginarymodel')) + apps.lazy_model_operation(dummy_function, ("auth", "imaginarymodel")) + apps.lazy_model_operation(dummy_function, ("fanciful_app", "imaginarymodel")) - post_init.connect(dummy_function, sender='missing-app.Model', apps=apps) - post_init.connect(DummyClass(), sender='missing-app.Model', apps=apps) - post_init.connect(DummyClass().dummy_method, sender='missing-app.Model', apps=apps) + post_init.connect(dummy_function, sender="missing-app.Model", apps=apps) + post_init.connect(DummyClass(), sender="missing-app.Model", apps=apps) + post_init.connect( + DummyClass().dummy_method, sender="missing-app.Model", apps=apps + ) - self.assertEqual(_check_lazy_references(apps), [ - Error( - "%r contains a lazy reference to auth.imaginarymodel, " - "but app 'auth' doesn't provide model 'imaginarymodel'." % dummy_function, - obj=dummy_function, - id='models.E022', - ), - Error( - "%r contains a lazy reference to fanciful_app.imaginarymodel, " - "but app 'fanciful_app' isn't installed." % dummy_function, - obj=dummy_function, - id='models.E022', - ), - Error( - "An instance of class 'DummyClass' was connected to " - "the 'post_init' signal with a lazy reference to the sender " - "'missing-app.model', but app 'missing-app' isn't installed.", - hint=None, - obj='invalid_models_tests.test_models', - id='signals.E001', - ), - Error( - "Bound method 'DummyClass.dummy_method' was connected to the " - "'post_init' signal with a lazy reference to the sender " - "'missing-app.model', but app 'missing-app' isn't installed.", - hint=None, - obj='invalid_models_tests.test_models', - id='signals.E001', - ), - Error( - "The field invalid_models_tests.DummyModel.author was declared " - "with a lazy reference to 'invalid_models_tests.author', but app " - "'invalid_models_tests' isn't installed.", - hint=None, - obj=DummyModel.author.field, - id='fields.E307', - ), - Error( - "The function 'dummy_function' was connected to the 'post_init' " - "signal with a lazy reference to the sender " - "'missing-app.model', but app 'missing-app' isn't installed.", - hint=None, - obj='invalid_models_tests.test_models', - id='signals.E001', - ), - ]) + self.assertEqual( + _check_lazy_references(apps), + [ + Error( + "%r contains a lazy reference to auth.imaginarymodel, " + "but app 'auth' doesn't provide model 'imaginarymodel'." + % dummy_function, + obj=dummy_function, + id="models.E022", + ), + Error( + "%r contains a lazy reference to fanciful_app.imaginarymodel, " + "but app 'fanciful_app' isn't installed." % dummy_function, + obj=dummy_function, + id="models.E022", + ), + Error( + "An instance of class 'DummyClass' was connected to " + "the 'post_init' signal with a lazy reference to the sender " + "'missing-app.model', but app 'missing-app' isn't installed.", + hint=None, + obj="invalid_models_tests.test_models", + id="signals.E001", + ), + Error( + "Bound method 'DummyClass.dummy_method' was connected to the " + "'post_init' signal with a lazy reference to the sender " + "'missing-app.model', but app 'missing-app' isn't installed.", + hint=None, + obj="invalid_models_tests.test_models", + id="signals.E001", + ), + Error( + "The field invalid_models_tests.DummyModel.author was declared " + "with a lazy reference to 'invalid_models_tests.author', but app " + "'invalid_models_tests' isn't installed.", + hint=None, + obj=DummyModel.author.field, + id="fields.E307", + ), + Error( + "The function 'dummy_function' was connected to the 'post_init' " + "signal with a lazy reference to the sender " + "'missing-app.model', but app 'missing-app' isn't installed.", + hint=None, + obj="invalid_models_tests.test_models", + id="signals.E001", + ), + ], + ) class MultipleAutoFieldsTests(TestCase): @@ -1625,20 +1874,21 @@ class MultipleAutoFieldsTests(TestCase): "than one auto-generated field." ) with self.assertRaisesMessage(ValueError, msg): + class MultipleAutoFields(models.Model): auto1 = models.AutoField(primary_key=True) auto2 = models.AutoField(primary_key=True) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class JSONFieldTests(TestCase): - @skipUnlessDBFeature('supports_json_field') + @skipUnlessDBFeature("supports_json_field") def test_ordering_pointing_to_json_field_value(self): class Model(models.Model): field = models.JSONField() class Meta: - ordering = ['field__value'] + ordering = ["field__value"] self.assertEqual(Model.check(databases=self.databases), []) @@ -1647,9 +1897,9 @@ class JSONFieldTests(TestCase): field = models.JSONField() error = Error( - '%s does not support JSONFields.' % connection.display_name, + "%s does not support JSONFields." % connection.display_name, obj=Model, - id='fields.E180', + id="fields.E180", ) expected = [] if connection.features.supports_json_field else [error] self.assertEqual(Model.check(databases=self.databases), expected) @@ -1659,31 +1909,35 @@ class JSONFieldTests(TestCase): field = models.JSONField() class Meta: - required_db_features = {'supports_json_field'} + required_db_features = {"supports_json_field"} self.assertEqual(Model.check(databases=self.databases), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ConstraintsTests(TestCase): def test_check_constraints(self): class Model(models.Model): age = models.IntegerField() class Meta: - constraints = [models.CheckConstraint(check=models.Q(age__gte=18), name='is_adult')] + constraints = [ + models.CheckConstraint(check=models.Q(age__gte=18), name="is_adult") + ] errors = Model.check(databases=self.databases) warn = Warning( - '%s does not support check constraints.' % connection.display_name, + "%s does not support check constraints." % connection.display_name, hint=( "A constraint won't be created. Silence this warning if you " "don't care about it." ), obj=Model, - id='models.W027', + id="models.W027", + ) + expected = ( + [] if connection.features.supports_table_check_constraints else [warn] ) - expected = [] if connection.features.supports_table_check_constraints else [warn] self.assertCountEqual(errors, expected) def test_check_constraints_required_db_features(self): @@ -1691,106 +1945,124 @@ class ConstraintsTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_table_check_constraints'} - constraints = [models.CheckConstraint(check=models.Q(age__gte=18), name='is_adult')] + required_db_features = {"supports_table_check_constraints"} + constraints = [ + models.CheckConstraint(check=models.Q(age__gte=18), name="is_adult") + ] + self.assertEqual(Model.check(databases=self.databases), []) def test_check_constraint_pointing_to_missing_field(self): class Model(models.Model): class Meta: - required_db_features = {'supports_table_check_constraints'} + required_db_features = {"supports_table_check_constraints"} constraints = [ models.CheckConstraint( - name='name', check=models.Q(missing_field=2), + name="name", + check=models.Q(missing_field=2), ), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ] if connection.features.supports_table_check_constraints else []) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ] + if connection.features.supports_table_check_constraints + else [], + ) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_reverse_fk(self): class Model(models.Model): - parent = models.ForeignKey('self', models.CASCADE, related_name='parents') + parent = models.ForeignKey("self", models.CASCADE, related_name="parents") class Meta: constraints = [ - models.CheckConstraint(name='name', check=models.Q(parents=3)), + models.CheckConstraint(name="name", check=models.Q(parents=3)), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the nonexistent field 'parents'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'parents'.", + obj=Model, + id="models.E012", + ), + ], + ) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_reverse_o2o(self): class Model(models.Model): - parent = models.OneToOneField('self', models.CASCADE) + parent = models.OneToOneField("self", models.CASCADE) class Meta: constraints = [ models.CheckConstraint( - name='name', + name="name", check=models.Q(model__isnull=True), ), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the nonexistent field 'model'.", - obj=Model, - id='models.E012', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'model'.", + obj=Model, + id="models.E012", + ), + ], + ) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: constraints = [ - models.CheckConstraint(name='name', check=models.Q(m2m=2)), + models.CheckConstraint(name="name", check=models.Q(m2m=2)), ] - 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', - ), - ]) + 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_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_fk(self): class Target(models.Model): pass class Model(models.Model): - fk_1 = models.ForeignKey(Target, models.CASCADE, related_name='target_1') - fk_2 = models.ForeignKey(Target, models.CASCADE, related_name='target_2') + fk_1 = models.ForeignKey(Target, models.CASCADE, related_name="target_1") + fk_2 = models.ForeignKey(Target, models.CASCADE, related_name="target_2") class Meta: constraints = [ models.CheckConstraint( - name='name', + name="name", check=models.Q(fk_1_id=2) | models.Q(fk_2=2), ), ] self.assertEqual(Model.check(databases=self.databases), []) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_pk(self): class Model(models.Model): age = models.SmallIntegerField() @@ -1798,14 +2070,14 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.CheckConstraint( - name='name', - check=models.Q(pk__gt=5) & models.Q(age__gt=models.F('pk')), + name="name", + check=models.Q(pk__gt=5) & models.Q(age__gt=models.F("pk")), ), ] self.assertEqual(Model.check(databases=self.databases), []) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_non_local_field(self): class Parent(models.Model): field1 = models.IntegerField() @@ -1815,99 +2087,111 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.CheckConstraint(name='name', check=models.Q(field1=1)), + models.CheckConstraint(name="name", check=models.Q(field1=1)), ] - self.assertEqual(Child.check(databases=self.databases), [ - Error( - "'constraints' refers to field 'field1' which is not local to " - "model 'Child'.", - hint='This issue may be caused by multi-table inheritance.', - obj=Child, - id='models.E016', - ), - ]) + self.assertEqual( + Child.check(databases=self.databases), + [ + Error( + "'constraints' refers to field 'field1' which is not local to " + "model 'Child'.", + hint="This issue may be caused by multi-table inheritance.", + obj=Child, + id="models.E016", + ), + ], + ) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_joined_fields(self): class Model(models.Model): name = models.CharField(max_length=10) field1 = models.PositiveSmallIntegerField() field2 = models.PositiveSmallIntegerField() field3 = models.PositiveSmallIntegerField() - parent = models.ForeignKey('self', models.CASCADE) - previous = models.OneToOneField('self', models.CASCADE, related_name='next') + parent = models.ForeignKey("self", models.CASCADE) + previous = models.OneToOneField("self", models.CASCADE, related_name="next") class Meta: constraints = [ models.CheckConstraint( - name='name1', check=models.Q( - field1__lt=models.F('parent__field1') + models.F('parent__field2') - ) + name="name1", + check=models.Q( + field1__lt=models.F("parent__field1") + + models.F("parent__field2") + ), ), models.CheckConstraint( - name='name2', check=models.Q(name=Lower('parent__name')) + name="name2", check=models.Q(name=Lower("parent__name")) ), models.CheckConstraint( - name='name3', check=models.Q(parent__field3=models.F('field1')) + name="name3", check=models.Q(parent__field3=models.F("field1")) ), models.CheckConstraint( - name='name4', check=models.Q(name=Lower('previous__name')), + name="name4", + check=models.Q(name=Lower("previous__name")), ), ] joined_fields = [ - 'parent__field1', - 'parent__field2', - 'parent__field3', - 'parent__name', - 'previous__name', + "parent__field1", + "parent__field2", + "parent__field3", + "parent__name", + "previous__name", ] errors = Model.check(databases=self.databases) expected_errors = [ Error( "'constraints' refers to the joined field '%s'." % field_name, obj=Model, - id='models.E041', - ) for field_name in joined_fields + id="models.E041", + ) + for field_name in joined_fields ] self.assertCountEqual(errors, expected_errors) - @skipUnlessDBFeature('supports_table_check_constraints') + @skipUnlessDBFeature("supports_table_check_constraints") def test_check_constraint_pointing_to_joined_fields_complex_check(self): class Model(models.Model): name = models.PositiveSmallIntegerField() field1 = models.PositiveSmallIntegerField() field2 = models.PositiveSmallIntegerField() - parent = models.ForeignKey('self', models.CASCADE) + parent = models.ForeignKey("self", models.CASCADE) class Meta: constraints = [ models.CheckConstraint( - name='name', + name="name", check=models.Q( ( - models.Q(name='test') & - models.Q(field1__lt=models.F('parent__field1')) - ) | - ( - models.Q(name__startswith=Lower('parent__name')) & - models.Q(field1__gte=( - models.F('parent__field1') + models.F('parent__field2') - )) + models.Q(name="test") + & models.Q(field1__lt=models.F("parent__field1")) + ) + | ( + models.Q(name__startswith=Lower("parent__name")) + & models.Q( + field1__gte=( + models.F("parent__field1") + + models.F("parent__field2") + ) + ) ) - ) | (models.Q(name='test1')) + ) + | (models.Q(name="test1")), ), ] - joined_fields = ['parent__field1', 'parent__field2', 'parent__name'] + joined_fields = ["parent__field1", "parent__field2", "parent__name"] errors = Model.check(databases=self.databases) expected_errors = [ Error( "'constraints' refers to the joined field '%s'." % field_name, obj=Model, - id='models.E041', - ) for field_name in joined_fields + id="models.E041", + ) + for field_name in joined_fields ] self.assertCountEqual(errors, expected_errors) @@ -1918,25 +2202,29 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_gte_100', + fields=["age"], + name="unique_age_gte_100", condition=models.Q(age__gte=100), ), ] errors = Model.check(databases=self.databases) - expected = [] if connection.features.supports_partial_indexes else [ - Warning( - '%s does not support unique constraints with conditions.' - % connection.display_name, - hint=( - "A constraint won't be created. Silence this warning if " - "you don't care about it." + expected = ( + [] + if connection.features.supports_partial_indexes + else [ + Warning( + "%s does not support unique constraints with conditions." + % connection.display_name, + hint=( + "A constraint won't be created. Silence this warning if " + "you don't care about it." + ), + obj=Model, + id="models.W036", ), - obj=Model, - id='models.W036', - ), - ] + ] + ) self.assertEqual(errors, expected) def test_unique_constraint_with_condition_required_db_features(self): @@ -1944,11 +2232,11 @@ class ConstraintsTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_partial_indexes'} + required_db_features = {"supports_partial_indexes"} constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_gte_100', + fields=["age"], + name="unique_age_gte_100", condition=models.Q(age__gte=100), ), ] @@ -1960,67 +2248,82 @@ class ConstraintsTests(TestCase): age = models.SmallIntegerField() class Meta: - required_db_features = {'supports_partial_indexes'} + required_db_features = {"supports_partial_indexes"} constraints = [ models.UniqueConstraint( - name='name', - fields=['age'], + name="name", + fields=["age"], condition=models.Q(missing_field=2), ), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the nonexistent field 'missing_field'.", - obj=Model, - id='models.E012', - ), - ] if connection.features.supports_partial_indexes else []) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ] + if connection.features.supports_partial_indexes + else [], + ) def test_unique_constraint_condition_pointing_to_joined_fields(self): class Model(models.Model): age = models.SmallIntegerField() - parent = models.ForeignKey('self', models.CASCADE) + parent = models.ForeignKey("self", models.CASCADE) class Meta: - required_db_features = {'supports_partial_indexes'} + required_db_features = {"supports_partial_indexes"} constraints = [ models.UniqueConstraint( - name='name', - fields=['age'], + name="name", + fields=["age"], condition=models.Q(parent__age__lt=2), ), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the joined field 'parent__age__lt'.", - obj=Model, - id='models.E041', - ) - ] if connection.features.supports_partial_indexes else []) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the joined field 'parent__age__lt'.", + obj=Model, + id="models.E041", + ) + ] + if connection.features.supports_partial_indexes + else [], + ) def test_unique_constraint_pointing_to_reverse_o2o(self): class Model(models.Model): - parent = models.OneToOneField('self', models.CASCADE) + parent = models.OneToOneField("self", models.CASCADE) class Meta: - required_db_features = {'supports_partial_indexes'} + required_db_features = {"supports_partial_indexes"} constraints = [ models.UniqueConstraint( - fields=['parent'], - name='name', + fields=["parent"], + name="name", condition=models.Q(model__isnull=True), ), ] - self.assertEqual(Model.check(databases=self.databases), [ - Error( - "'constraints' refers to the nonexistent field 'model'.", - obj=Model, - id='models.E012', - ), - ] if connection.features.supports_partial_indexes else []) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'model'.", + obj=Model, + id="models.E012", + ), + ] + if connection.features.supports_partial_indexes + else [], + ) def test_deferrable_unique_constraint(self): class Model(models.Model): @@ -2029,25 +2332,29 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_deferrable', + fields=["age"], + name="unique_age_deferrable", deferrable=models.Deferrable.DEFERRED, ), ] errors = Model.check(databases=self.databases) - expected = [] if connection.features.supports_deferrable_unique_constraints else [ - Warning( - '%s does not support deferrable unique constraints.' - % connection.display_name, - hint=( - "A constraint won't be created. Silence this warning if " - "you don't care about it." + expected = ( + [] + if connection.features.supports_deferrable_unique_constraints + else [ + Warning( + "%s does not support deferrable unique constraints." + % connection.display_name, + hint=( + "A constraint won't be created. Silence this warning if " + "you don't care about it." + ), + obj=Model, + id="models.W038", ), - obj=Model, - id='models.W038', - ), - ] + ] + ) self.assertEqual(errors, expected) def test_deferrable_unique_constraint_required_db_features(self): @@ -2055,11 +2362,11 @@ class ConstraintsTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_deferrable_unique_constraints'} + required_db_features = {"supports_deferrable_unique_constraints"} constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_deferrable', + fields=["age"], + name="unique_age_deferrable", deferrable=models.Deferrable.IMMEDIATE, ), ] @@ -2069,31 +2376,39 @@ class ConstraintsTests(TestCase): def test_unique_constraint_pointing_to_missing_field(self): class Model(models.Model): class Meta: - constraints = [models.UniqueConstraint(fields=['missing_field'], name='name')] + constraints = [ + models.UniqueConstraint(fields=["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', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) def test_unique_constraint_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - constraints = [models.UniqueConstraint(fields=['m2m'], name='name')] + constraints = [models.UniqueConstraint(fields=["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', - ), - ]) + 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", + ), + ], + ) def test_unique_constraint_pointing_to_non_local_field(self): class Parent(models.Model): @@ -2104,30 +2419,33 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.UniqueConstraint(fields=['field2', 'field1'], name='name'), + models.UniqueConstraint(fields=["field2", "field1"], name="name"), ] - self.assertEqual(Child.check(databases=self.databases), [ - Error( - "'constraints' refers to field 'field1' which is not local to " - "model 'Child'.", - hint='This issue may be caused by multi-table inheritance.', - obj=Child, - id='models.E016', - ), - ]) + self.assertEqual( + Child.check(databases=self.databases), + [ + Error( + "'constraints' refers to field 'field1' which is not local to " + "model 'Child'.", + hint="This issue may be caused by multi-table inheritance.", + obj=Child, + id="models.E016", + ), + ], + ) def test_unique_constraint_pointing_to_fk(self): class Target(models.Model): pass class Model(models.Model): - fk_1 = models.ForeignKey(Target, models.CASCADE, related_name='target_1') - fk_2 = models.ForeignKey(Target, models.CASCADE, related_name='target_2') + fk_1 = models.ForeignKey(Target, models.CASCADE, related_name="target_1") + fk_2 = models.ForeignKey(Target, models.CASCADE, related_name="target_2") class Meta: constraints = [ - models.UniqueConstraint(fields=['fk_1_id', 'fk_2'], name='name'), + models.UniqueConstraint(fields=["fk_1_id", "fk_2"], name="name"), ] self.assertEqual(Model.check(databases=self.databases), []) @@ -2139,25 +2457,29 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_include_id', - include=['id'], + fields=["age"], + name="unique_age_include_id", + include=["id"], ), ] errors = Model.check(databases=self.databases) - expected = [] if connection.features.supports_covering_indexes else [ - Warning( - '%s does not support unique constraints with non-key columns.' - % connection.display_name, - hint=( - "A constraint won't be created. Silence this warning if " - "you don't care about it." + expected = ( + [] + if connection.features.supports_covering_indexes + else [ + Warning( + "%s does not support unique constraints with non-key columns." + % connection.display_name, + hint=( + "A constraint won't be created. Silence this warning if " + "you don't care about it." + ), + obj=Model, + id="models.W039", ), - obj=Model, - id='models.W039', - ), - ] + ] + ) self.assertEqual(errors, expected) def test_unique_constraint_with_include_required_db_features(self): @@ -2165,61 +2487,67 @@ class ConstraintsTests(TestCase): age = models.IntegerField() class Meta: - required_db_features = {'supports_covering_indexes'} + required_db_features = {"supports_covering_indexes"} constraints = [ models.UniqueConstraint( - fields=['age'], - name='unique_age_include_id', - include=['id'], + fields=["age"], + name="unique_age_include_id", + include=["id"], ), ] self.assertEqual(Model.check(databases=self.databases), []) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_unique_constraint_include_pointing_to_missing_field(self): class Model(models.Model): class Meta: constraints = [ models.UniqueConstraint( - fields=['id'], - include=['missing_field'], - name='name', + fields=["id"], + include=["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', - ), - ]) + self.assertEqual( + Model.check(databases=self.databases), + [ + Error( + "'constraints' refers to the nonexistent field 'missing_field'.", + obj=Model, + id="models.E012", + ), + ], + ) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_unique_constraint_include_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: constraints = [ models.UniqueConstraint( - fields=['id'], - include=['m2m'], - name='name', + fields=["id"], + include=["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', - ), - ]) + 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_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_unique_constraint_include_pointing_to_non_local_field(self): class Parent(models.Model): field1 = models.IntegerField() @@ -2230,37 +2558,40 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['field2'], - include=['field1'], - name='name', + fields=["field2"], + include=["field1"], + name="name", ), ] - self.assertEqual(Child.check(databases=self.databases), [ - Error( - "'constraints' refers to field 'field1' which is not local to " - "model 'Child'.", - hint='This issue may be caused by multi-table inheritance.', - obj=Child, - id='models.E016', - ), - ]) + self.assertEqual( + Child.check(databases=self.databases), + [ + Error( + "'constraints' refers to field 'field1' which is not local to " + "model 'Child'.", + hint="This issue may be caused by multi-table inheritance.", + obj=Child, + id="models.E016", + ), + ], + ) - @skipUnlessDBFeature('supports_covering_indexes') + @skipUnlessDBFeature("supports_covering_indexes") def test_unique_constraint_include_pointing_to_fk(self): class Target(models.Model): pass class Model(models.Model): - fk_1 = models.ForeignKey(Target, models.CASCADE, related_name='target_1') - fk_2 = models.ForeignKey(Target, models.CASCADE, related_name='target_2') + fk_1 = models.ForeignKey(Target, models.CASCADE, related_name="target_1") + fk_2 = models.ForeignKey(Target, models.CASCADE, related_name="target_2") class Meta: constraints = [ models.UniqueConstraint( - fields=['id'], - include=['fk_1_id', 'fk_2'], - name='name', + fields=["id"], + include=["fk_1_id", "fk_2"], + name="name", ), ] @@ -2272,18 +2603,18 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.UniqueConstraint(Lower('name'), name='lower_name_uq'), + models.UniqueConstraint(Lower("name"), name="lower_name_uq"), ] warn = Warning( - '%s does not support unique constraints on expressions.' + "%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', + id="models.W044", ) expected = [] if connection.features.supports_expression_indexes else [warn] self.assertEqual(Model.check(databases=self.databases), expected) @@ -2294,13 +2625,13 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.UniqueConstraint(Lower('name'), name='lower_name_unq'), + models.UniqueConstraint(Lower("name"), name="lower_name_unq"), ] - required_db_features = {'supports_expression_indexes'} + required_db_features = {"supports_expression_indexes"} self.assertEqual(Model.check(databases=self.databases), []) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_expression_custom_lookup(self): class Model(models.Model): height = models.IntegerField() @@ -2309,97 +2640,110 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ models.UniqueConstraint( - models.F('height') / (models.F('weight__abs') + models.Value(5)), - name='name', + 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') + @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'), + 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', - ), - ]) + 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') + @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'), + 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', - ), - ]) + 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') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_pointing_to_m2m_field(self): class Model(models.Model): - m2m = models.ManyToManyField('self') + m2m = models.ManyToManyField("self") class Meta: - constraints = [models.UniqueConstraint(Lower('m2m'), name='name')] + 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', - ), - ]) + 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') + @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')] + 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', - ), - ]) + 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') + @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') + 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', + Lower("foo_1_id"), + Lower("foo_2"), + name="name", ), ] |
