diff options
author | django-bot <ops@djangoproject.com> | 2022-02-03 20:24:19 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-02-07 20:37:05 +0100 |
commit | 9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch) | |
tree | f0506b668a013d0063e5fba3dbf4863b466713ba /tests/invalid_models_tests | |
parent | f68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff) | |
download | django-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz |
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/invalid_models_tests')
-rw-r--r-- | tests/invalid_models_tests/test_backend_specific.py | 19 | ||||
-rw-r--r-- | tests/invalid_models_tests/test_custom_fields.py | 5 | ||||
-rw-r--r-- | tests/invalid_models_tests/test_deprecated_fields.py | 78 | ||||
-rw-r--r-- | tests/invalid_models_tests/test_models.py | 2446 | ||||
-rw-r--r-- | tests/invalid_models_tests/test_ordinary_fields.py | 1085 | ||||
-rw-r--r-- | tests/invalid_models_tests/test_relative_fields.py | 2156 |
6 files changed, 3244 insertions, 2545 deletions
diff --git a/tests/invalid_models_tests/test_backend_specific.py b/tests/invalid_models_tests/test_backend_specific.py index 236909d50e..8461641c9a 100644 --- a/tests/invalid_models_tests/test_backend_specific.py +++ b/tests/invalid_models_tests/test_backend_specific.py @@ -9,20 +9,21 @@ from django.test.utils import isolate_apps def dummy_allow_migrate(db, app_label, **hints): # Prevent checks from being run on the 'other' database, which doesn't have # its check_field() method mocked in the test. - return db == 'default' + return db == "default" -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class BackendSpecificChecksTests(SimpleTestCase): - - @mock.patch('django.db.models.fields.router.allow_migrate', new=dummy_allow_migrate) + @mock.patch("django.db.models.fields.router.allow_migrate", new=dummy_allow_migrate) def test_check_field(self): - """ Test if backend specific checks are performed. """ - error = Error('an error') + """Test if backend specific checks are performed.""" + error = Error("an error") class Model(models.Model): field = models.IntegerField() - field = Model._meta.get_field('field') - with mock.patch.object(connections['default'].validation, 'check_field', return_value=[error]): - self.assertEqual(field.check(databases={'default'}), [error]) + field = Model._meta.get_field("field") + with mock.patch.object( + connections["default"].validation, "check_field", return_value=[error] + ): + self.assertEqual(field.check(databases={"default"}), [error]) diff --git a/tests/invalid_models_tests/test_custom_fields.py b/tests/invalid_models_tests/test_custom_fields.py index af14bae569..cfca888bec 100644 --- a/tests/invalid_models_tests/test_custom_fields.py +++ b/tests/invalid_models_tests/test_custom_fields.py @@ -3,9 +3,8 @@ from django.test import SimpleTestCase from django.test.utils import isolate_apps -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class CustomFieldTest(SimpleTestCase): - def test_none_column(self): class NoColumnField(models.AutoField): def db_type(self, connection): @@ -16,5 +15,5 @@ class CustomFieldTest(SimpleTestCase): field = NoColumnField(primary_key=True, db_column="other_field") other_field = models.IntegerField() - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) diff --git a/tests/invalid_models_tests/test_deprecated_fields.py b/tests/invalid_models_tests/test_deprecated_fields.py index a3ee618ce4..fee5fb39f8 100644 --- a/tests/invalid_models_tests/test_deprecated_fields.py +++ b/tests/invalid_models_tests/test_deprecated_fields.py @@ -6,7 +6,7 @@ from django.test import SimpleTestCase from django.test.utils import isolate_apps -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class DeprecatedFieldsTests(SimpleTestCase): def test_IPAddressField_deprecated(self): class IPAddressModel(models.Model): @@ -15,13 +15,15 @@ class DeprecatedFieldsTests(SimpleTestCase): model = IPAddressModel() self.assertEqual( model.check(), - [checks.Error( - 'IPAddressField has been removed except for support in ' - 'historical migrations.', - hint='Use GenericIPAddressField instead.', - obj=IPAddressModel._meta.get_field('ip'), - id='fields.E900', - )], + [ + checks.Error( + "IPAddressField has been removed except for support in " + "historical migrations.", + hint="Use GenericIPAddressField instead.", + obj=IPAddressModel._meta.get_field("ip"), + id="fields.E900", + ) + ], ) def test_CommaSeparatedIntegerField_deprecated(self): @@ -31,13 +33,15 @@ class DeprecatedFieldsTests(SimpleTestCase): model = CommaSeparatedIntegerModel() self.assertEqual( model.check(), - [checks.Error( - 'CommaSeparatedIntegerField is removed except for support in ' - 'historical migrations.', - hint='Use CharField(validators=[validate_comma_separated_integer_list]) instead.', - obj=CommaSeparatedIntegerModel._meta.get_field('csi'), - id='fields.E901', - )], + [ + checks.Error( + "CommaSeparatedIntegerField is removed except for support in " + "historical migrations.", + hint="Use CharField(validators=[validate_comma_separated_integer_list]) instead.", + obj=CommaSeparatedIntegerModel._meta.get_field("csi"), + id="fields.E901", + ) + ], ) def test_nullbooleanfield_deprecated(self): @@ -45,29 +49,35 @@ class DeprecatedFieldsTests(SimpleTestCase): nb = models.NullBooleanField() model = NullBooleanFieldModel() - self.assertEqual(model.check(), [ - checks.Error( - 'NullBooleanField is removed except for support in historical ' - 'migrations.', - hint='Use BooleanField(null=True) instead.', - obj=NullBooleanFieldModel._meta.get_field('nb'), - id='fields.E903', - ), - ]) + self.assertEqual( + model.check(), + [ + checks.Error( + "NullBooleanField is removed except for support in historical " + "migrations.", + hint="Use BooleanField(null=True) instead.", + obj=NullBooleanFieldModel._meta.get_field("nb"), + id="fields.E903", + ), + ], + ) - @skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL') + @skipUnless(connection.vendor == "postgresql", "PostgreSQL specific SQL") def test_postgres_jsonfield_deprecated(self): from django.contrib.postgres.fields import JSONField class PostgresJSONFieldModel(models.Model): field = JSONField() - self.assertEqual(PostgresJSONFieldModel.check(), [ - checks.Error( - 'django.contrib.postgres.fields.JSONField is removed except ' - 'for support in historical migrations.', - hint='Use django.db.models.JSONField instead.', - obj=PostgresJSONFieldModel._meta.get_field('field'), - id='fields.E904', - ), - ]) + self.assertEqual( + PostgresJSONFieldModel.check(), + [ + checks.Error( + "django.contrib.postgres.fields.JSONField is removed except " + "for support in historical migrations.", + hint="Use django.db.models.JSONField instead.", + obj=PostgresJSONFieldModel._meta.get_field("field"), + id="fields.E904", + ), + ], + ) 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' - - 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." + 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." + ), + 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') - - 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')] - ]) + 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")] + ], + ) 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", ), ] diff --git a/tests/invalid_models_tests/test_ordinary_fields.py b/tests/invalid_models_tests/test_ordinary_fields.py index 7797b24202..ba63cb2903 100644 --- a/tests/invalid_models_tests/test_ordinary_fields.py +++ b/tests/invalid_models_tests/test_ordinary_fields.py @@ -1,11 +1,10 @@ import unittest import uuid -from django.core.checks import Error, Warning as DjangoWarning +from django.core.checks import Error +from django.core.checks import Warning as DjangoWarning from django.db import connection, models -from django.test import ( - SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature, -) +from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test.utils import isolate_apps, override_settings from django.utils.functional import lazy from django.utils.timezone import now @@ -13,14 +12,13 @@ from django.utils.translation import gettext_lazy as _ from django.utils.version import get_docs_version -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class AutoFieldTests(SimpleTestCase): - def test_valid_case(self): class Model(models.Model): id = models.AutoField(primary_key=True) - field = Model._meta.get_field('id') + field = Model._meta.get_field("id") self.assertEqual(field.check(), []) def test_primary_key(self): @@ -33,167 +31,196 @@ class AutoFieldTests(SimpleTestCase): # AutoField. another = models.IntegerField(primary_key=True) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - 'AutoFields must set primary_key=True.', - obj=field, - id='fields.E100', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "AutoFields must set primary_key=True.", + obj=field, + id="fields.E100", + ), + ], + ) def test_max_length_warning(self): class Model(models.Model): auto = models.AutoField(primary_key=True, max_length=2) - field = Model._meta.get_field('auto') - self.assertEqual(field.check(), [ - DjangoWarning( - "'max_length' is ignored when used with %s." - % field.__class__.__name__, - hint="Remove 'max_length' from field", - obj=field, - id='fields.W122', - ), - ]) + field = Model._meta.get_field("auto") + self.assertEqual( + field.check(), + [ + DjangoWarning( + "'max_length' is ignored when used with %s." + % field.__class__.__name__, + hint="Remove 'max_length' from field", + obj=field, + id="fields.W122", + ), + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class BinaryFieldTests(SimpleTestCase): - def test_valid_default_value(self): class Model(models.Model): - field1 = models.BinaryField(default=b'test') + field1 = models.BinaryField(default=b"test") field2 = models.BinaryField(default=None) - for field_name in ('field1', 'field2'): + for field_name in ("field1", "field2"): field = Model._meta.get_field(field_name) self.assertEqual(field.check(), []) def test_str_default_value(self): class Model(models.Model): - field = models.BinaryField(default='test') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "BinaryField's default cannot be a string. Use bytes content " - "instead.", - obj=field, - id='fields.E170', - ), - ]) + field = models.BinaryField(default="test") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "BinaryField's default cannot be a string. Use bytes content " + "instead.", + obj=field, + id="fields.E170", + ), + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class CharFieldTests(TestCase): - def test_valid_field(self): class Model(models.Model): field = models.CharField( max_length=255, choices=[ - ('1', 'item1'), - ('2', 'item2'), + ("1", "item1"), + ("2", "item2"), ], db_index=True, ) - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) def test_missing_max_length(self): class Model(models.Model): field = models.CharField() - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "CharFields must define a 'max_length' attribute.", - obj=field, - id='fields.E120', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "CharFields must define a 'max_length' attribute.", + obj=field, + id="fields.E120", + ), + ], + ) def test_negative_max_length(self): class Model(models.Model): field = models.CharField(max_length=-1) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'max_length' must be a positive integer.", - obj=field, - id='fields.E121', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'max_length' must be a positive integer.", + obj=field, + id="fields.E121", + ), + ], + ) def test_bad_max_length_value(self): class Model(models.Model): field = models.CharField(max_length="bad") - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'max_length' must be a positive integer.", - obj=field, - id='fields.E121', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'max_length' must be a positive integer.", + obj=field, + id="fields.E121", + ), + ], + ) def test_str_max_length_value(self): class Model(models.Model): - field = models.CharField(max_length='20') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'max_length' must be a positive integer.", - obj=field, - id='fields.E121', - ), - ]) + field = models.CharField(max_length="20") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'max_length' must be a positive integer.", + obj=field, + id="fields.E121", + ), + ], + ) def test_str_max_length_type(self): class Model(models.Model): field = models.CharField(max_length=True) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'max_length' must be a positive integer.", - obj=field, - id='fields.E121' - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'max_length' must be a positive integer.", + obj=field, + id="fields.E121", + ), + ], + ) def test_non_iterable_choices(self): class Model(models.Model): - field = models.CharField(max_length=10, choices='bad') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'choices' must be an iterable (e.g., a list or tuple).", - obj=field, - id='fields.E004', - ), - ]) + field = models.CharField(max_length=10, choices="bad") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'choices' must be an iterable (e.g., a list or tuple).", + obj=field, + id="fields.E004", + ), + ], + ) def test_non_iterable_choices_two_letters(self): """Two letters isn't a valid choice pair.""" - class Model(models.Model): - field = models.CharField(max_length=10, choices=['ab']) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'choices' must be an iterable containing (actual value, " - "human readable name) tuples.", - obj=field, - id='fields.E005', - ), - ]) + class Model(models.Model): + field = models.CharField(max_length=10, choices=["ab"]) + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'choices' must be an iterable containing (actual value, " + "human readable name) tuples.", + obj=field, + id="fields.E005", + ), + ], + ) def test_iterable_of_iterable_choices(self): class ThingItem: @@ -214,7 +241,7 @@ class CharFieldTests(TestCase): class ThingWithIterableChoices(models.Model): thing = models.CharField(max_length=100, blank=True, choices=Things()) - self.assertEqual(ThingWithIterableChoices._meta.get_field('thing').check(), []) + self.assertEqual(ThingWithIterableChoices._meta.get_field("thing").check(), []) def test_choices_containing_non_pairs(self): class Model(models.Model): @@ -225,215 +252,255 @@ class CharFieldTests(TestCase): for model in (Model, Model2): with self.subTest(model.__name__): - field = model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'choices' must be an iterable containing (actual " - "value, human readable name) tuples.", - obj=field, - id='fields.E005', - ), - ]) + field = model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'choices' must be an iterable containing (actual " + "value, human readable name) tuples.", + obj=field, + id="fields.E005", + ), + ], + ) def test_choices_containing_lazy(self): class Model(models.Model): - field = models.CharField(max_length=10, choices=[['1', _('1')], ['2', _('2')]]) + field = models.CharField( + max_length=10, choices=[["1", _("1")], ["2", _("2")]] + ) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_lazy_choices(self): class Model(models.Model): - field = models.CharField(max_length=10, choices=lazy(lambda: [[1, '1'], [2, '2']], tuple)()) + field = models.CharField( + max_length=10, choices=lazy(lambda: [[1, "1"], [2, "2"]], tuple)() + ) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_choices_named_group(self): class Model(models.Model): field = models.CharField( - max_length=10, choices=[ - ['knights', [['L', 'Lancelot'], ['G', 'Galahad']]], - ['wizards', [['T', 'Tim the Enchanter']]], - ['R', 'Random character'], + max_length=10, + choices=[ + ["knights", [["L", "Lancelot"], ["G", "Galahad"]]], + ["wizards", [["T", "Tim the Enchanter"]]], + ["R", "Random character"], ], ) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_choices_named_group_non_pairs(self): class Model(models.Model): field = models.CharField( max_length=10, - choices=[['knights', [['L', 'Lancelot', 'Du Lac']]]], + choices=[["knights", [["L", "Lancelot", "Du Lac"]]]], ) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'choices' must be an iterable containing (actual value, " - "human readable name) tuples.", - obj=field, - id='fields.E005', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'choices' must be an iterable containing (actual value, " + "human readable name) tuples.", + obj=field, + id="fields.E005", + ), + ], + ) def test_choices_named_group_bad_structure(self): class Model(models.Model): field = models.CharField( - max_length=10, choices=[ - ['knights', [ - ['Noble', [['G', 'Galahad']]], - ['Combative', [['L', 'Lancelot']]], - ]], + max_length=10, + choices=[ + [ + "knights", + [ + ["Noble", [["G", "Galahad"]]], + ["Combative", [["L", "Lancelot"]]], + ], + ], ], ) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'choices' must be an iterable containing (actual value, " - "human readable name) tuples.", - obj=field, - id='fields.E005', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'choices' must be an iterable containing (actual value, " + "human readable name) tuples.", + obj=field, + id="fields.E005", + ), + ], + ) def test_choices_named_group_lazy(self): class Model(models.Model): field = models.CharField( - max_length=10, choices=[ - [_('knights'), [['L', _('Lancelot')], ['G', _('Galahad')]]], - ['R', _('Random character')], + max_length=10, + choices=[ + [_("knights"), [["L", _("Lancelot")], ["G", _("Galahad")]]], + ["R", _("Random character")], ], ) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_choices_in_max_length(self): class Model(models.Model): field = models.CharField( - max_length=2, choices=[ - ('ABC', 'Value Too Long!'), ('OK', 'Good') - ], + max_length=2, + choices=[("ABC", "Value Too Long!"), ("OK", "Good")], ) group = models.CharField( - max_length=2, choices=[ - ('Nested', [('OK', 'Good'), ('Longer', 'Longer')]), - ('Grouped', [('Bad', 'Bad')]), + max_length=2, + choices=[ + ("Nested", [("OK", "Good"), ("Longer", "Longer")]), + ("Grouped", [("Bad", "Bad")]), ], ) - for name, choice_max_length in (('field', 3), ('group', 6)): + for name, choice_max_length in (("field", 3), ("group", 6)): with self.subTest(name): field = Model._meta.get_field(name) - self.assertEqual(field.check(), [ - Error( - "'max_length' is too small to fit the longest value " - "in 'choices' (%d characters)." % choice_max_length, - obj=field, - id='fields.E009', - ), - ]) + self.assertEqual( + field.check(), + [ + Error( + "'max_length' is too small to fit the longest value " + "in 'choices' (%d characters)." % choice_max_length, + obj=field, + id="fields.E009", + ), + ], + ) def test_bad_db_index_value(self): class Model(models.Model): - field = models.CharField(max_length=10, db_index='bad') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'db_index' must be None, True or False.", - obj=field, - id='fields.E006', - ), - ]) + field = models.CharField(max_length=10, db_index="bad") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'db_index' must be None, True or False.", + obj=field, + id="fields.E006", + ), + ], + ) def test_bad_validators(self): class Model(models.Model): field = models.CharField(max_length=10, validators=[True]) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "All 'validators' must be callable.", - hint=( - "validators[0] (True) isn't a function or instance of a " - "validator class." + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "All 'validators' must be callable.", + hint=( + "validators[0] (True) isn't a function or instance of a " + "validator class." + ), + obj=field, + id="fields.E008", ), - obj=field, - id='fields.E008', - ), - ]) + ], + ) - @unittest.skipUnless(connection.vendor == 'mysql', - "Test valid only for MySQL") + @unittest.skipUnless(connection.vendor == "mysql", "Test valid only for MySQL") def test_too_long_char_field_under_mysql(self): from django.db.backends.mysql.validation import DatabaseValidation class Model(models.Model): field = models.CharField(unique=True, max_length=256) - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") validator = DatabaseValidation(connection=connection) - self.assertEqual(validator.check_field(field), [ - DjangoWarning( - '%s may not allow unique CharFields to have a max_length > ' - '255.' % connection.display_name, - hint=( - 'See: https://docs.djangoproject.com/en/%s/ref/databases/' - '#mysql-character-fields' % get_docs_version() - ), - obj=field, - id='mysql.W003', - ) - ]) + self.assertEqual( + validator.check_field(field), + [ + DjangoWarning( + "%s may not allow unique CharFields to have a max_length > " + "255." % connection.display_name, + hint=( + "See: https://docs.djangoproject.com/en/%s/ref/databases/" + "#mysql-character-fields" % get_docs_version() + ), + obj=field, + id="mysql.W003", + ) + ], + ) def test_db_collation(self): class Model(models.Model): - field = models.CharField(max_length=100, db_collation='anything') + field = models.CharField(max_length=100, db_collation="anything") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") error = Error( - '%s does not support a database collation on CharFields.' + "%s does not support a database collation on CharFields." % connection.display_name, - id='fields.E190', + id="fields.E190", obj=field, ) - expected = [] if connection.features.supports_collation_on_charfield else [error] + expected = ( + [] if connection.features.supports_collation_on_charfield else [error] + ) self.assertEqual(field.check(databases=self.databases), expected) def test_db_collation_required_db_features(self): class Model(models.Model): - field = models.CharField(max_length=100, db_collation='anything') + field = models.CharField(max_length=100, db_collation="anything") class Meta: - required_db_features = {'supports_collation_on_charfield'} + required_db_features = {"supports_collation_on_charfield"} - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(databases=self.databases), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class DateFieldTests(SimpleTestCase): maxDiff = None def test_auto_now_and_auto_now_add_raise_error(self): class Model(models.Model): field0 = models.DateTimeField(auto_now=True, auto_now_add=True, default=now) - field1 = models.DateTimeField(auto_now=True, auto_now_add=False, default=now) - field2 = models.DateTimeField(auto_now=False, auto_now_add=True, default=now) - field3 = models.DateTimeField(auto_now=True, auto_now_add=True, default=None) + field1 = models.DateTimeField( + auto_now=True, auto_now_add=False, default=now + ) + field2 = models.DateTimeField( + auto_now=False, auto_now_add=True, default=now + ) + field3 = models.DateTimeField( + auto_now=True, auto_now_add=True, default=None + ) expected = [] checks = [] for i in range(4): - field = Model._meta.get_field('field%d' % i) - expected.append(Error( - "The options auto_now, auto_now_add, and default " - "are mutually exclusive. Only one of these options " - "may be present.", - obj=field, - id='fields.E160', - )) + field = Model._meta.get_field("field%d" % i) + expected.append( + Error( + "The options auto_now, auto_now_add, and default " + "are mutually exclusive. Only one of these options " + "may be present.", + obj=field, + id="fields.E160", + ) + ) checks.extend(field.check()) self.assertEqual(checks, expected) @@ -443,39 +510,42 @@ class DateFieldTests(SimpleTestCase): field_d = models.DateField(default=now().date()) field_now = models.DateField(default=now) - field_dt = Model._meta.get_field('field_dt') - field_d = Model._meta.get_field('field_d') - field_now = Model._meta.get_field('field_now') + field_dt = Model._meta.get_field("field_dt") + field_d = Model._meta.get_field("field_d") + field_now = Model._meta.get_field("field_now") errors = field_dt.check() errors.extend(field_d.check()) errors.extend(field_now.check()) # doesn't raise a warning - self.assertEqual(errors, [ - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=field_dt, - id='fields.W161', - ), - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=field_d, - id='fields.W161', - ) - ]) + self.assertEqual( + errors, + [ + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=field_dt, + id="fields.W161", + ), + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=field_d, + id="fields.W161", + ), + ], + ) @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class DateTimeFieldTests(SimpleTestCase): maxDiff = None @@ -485,209 +555,233 @@ class DateTimeFieldTests(SimpleTestCase): field_d = models.DateTimeField(default=now().date()) field_now = models.DateTimeField(default=now) - field_dt = Model._meta.get_field('field_dt') - field_d = Model._meta.get_field('field_d') - field_now = Model._meta.get_field('field_now') + field_dt = Model._meta.get_field("field_dt") + field_d = Model._meta.get_field("field_d") + field_now = Model._meta.get_field("field_now") errors = field_dt.check() errors.extend(field_d.check()) errors.extend(field_now.check()) # doesn't raise a warning - self.assertEqual(errors, [ - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=field_dt, - id='fields.W161', - ), - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=field_d, - id='fields.W161', - ) - ]) + self.assertEqual( + errors, + [ + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=field_dt, + id="fields.W161", + ), + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=field_d, + id="fields.W161", + ), + ], + ) @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class DecimalFieldTests(SimpleTestCase): - def test_required_attributes(self): class Model(models.Model): field = models.DecimalField() - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "DecimalFields must define a 'decimal_places' attribute.", - obj=field, - id='fields.E130', - ), - Error( - "DecimalFields must define a 'max_digits' attribute.", - obj=field, - id='fields.E132', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "DecimalFields must define a 'decimal_places' attribute.", + obj=field, + id="fields.E130", + ), + Error( + "DecimalFields must define a 'max_digits' attribute.", + obj=field, + id="fields.E132", + ), + ], + ) def test_negative_max_digits_and_decimal_places(self): class Model(models.Model): field = models.DecimalField(max_digits=-1, decimal_places=-1) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'decimal_places' must be a non-negative integer.", - obj=field, - id='fields.E131', - ), - Error( - "'max_digits' must be a positive integer.", - obj=field, - id='fields.E133', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'decimal_places' must be a non-negative integer.", + obj=field, + id="fields.E131", + ), + Error( + "'max_digits' must be a positive integer.", + obj=field, + id="fields.E133", + ), + ], + ) def test_bad_values_of_max_digits_and_decimal_places(self): class Model(models.Model): field = models.DecimalField(max_digits="bad", decimal_places="bad") - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'decimal_places' must be a non-negative integer.", - obj=field, - id='fields.E131', - ), - Error( - "'max_digits' must be a positive integer.", - obj=field, - id='fields.E133', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'decimal_places' must be a non-negative integer.", + obj=field, + id="fields.E131", + ), + Error( + "'max_digits' must be a positive integer.", + obj=field, + id="fields.E133", + ), + ], + ) def test_decimal_places_greater_than_max_digits(self): class Model(models.Model): field = models.DecimalField(max_digits=9, decimal_places=10) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'max_digits' must be greater or equal to 'decimal_places'.", - obj=field, - id='fields.E134', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'max_digits' must be greater or equal to 'decimal_places'.", + obj=field, + id="fields.E134", + ), + ], + ) def test_valid_field(self): class Model(models.Model): field = models.DecimalField(max_digits=10, decimal_places=10) - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class FileFieldTests(SimpleTestCase): - def test_valid_default_case(self): class Model(models.Model): field = models.FileField() - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_valid_case(self): class Model(models.Model): - field = models.FileField(upload_to='somewhere') + field = models.FileField(upload_to="somewhere") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) def test_primary_key(self): class Model(models.Model): - field = models.FileField(primary_key=False, upload_to='somewhere') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'primary_key' is not a valid argument for a FileField.", - obj=field, - id='fields.E201', - ) - ]) + field = models.FileField(primary_key=False, upload_to="somewhere") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'primary_key' is not a valid argument for a FileField.", + obj=field, + id="fields.E201", + ) + ], + ) def test_upload_to_starts_with_slash(self): class Model(models.Model): - field = models.FileField(upload_to='/somewhere') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "FileField's 'upload_to' argument must be a relative path, not " - "an absolute path.", - obj=field, - id='fields.E202', - hint='Remove the leading slash.', - ) - ]) + field = models.FileField(upload_to="/somewhere") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "FileField's 'upload_to' argument must be a relative path, not " + "an absolute path.", + obj=field, + id="fields.E202", + hint="Remove the leading slash.", + ) + ], + ) def test_upload_to_callable_not_checked(self): def callable(instance, filename): - return '/' + filename + return "/" + filename class Model(models.Model): field = models.FileField(upload_to=callable) - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class FilePathFieldTests(SimpleTestCase): - def test_forbidden_files_and_folders(self): class Model(models.Model): field = models.FilePathField(allow_files=False, allow_folders=False) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.", - obj=field, - id='fields.E140', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.", + obj=field, + id="fields.E140", + ), + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class GenericIPAddressFieldTests(SimpleTestCase): - def test_non_nullable_blank(self): class Model(models.Model): field = models.GenericIPAddressField(null=False, blank=True) - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - ('GenericIPAddressFields cannot have blank=True if null=False, ' - 'as blank values are stored as nulls.'), - obj=field, - id='fields.E150', - ), - ]) + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + ( + "GenericIPAddressFields cannot have blank=True if null=False, " + "as blank values are stored as nulls." + ), + obj=field, + id="fields.E150", + ), + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ImageFieldTests(SimpleTestCase): - def test_pillow_installed(self): try: from PIL import Image # NOQA @@ -697,25 +791,30 @@ class ImageFieldTests(SimpleTestCase): pillow_installed = True class Model(models.Model): - field = models.ImageField(upload_to='somewhere') + field = models.ImageField(upload_to="somewhere") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") errors = field.check() - expected = [] if pillow_installed else [ - Error( - 'Cannot use ImageField because Pillow is not installed.', - hint=('Get Pillow at https://pypi.org/project/Pillow/ ' - 'or run command "python -m pip install Pillow".'), - obj=field, - id='fields.E210', - ), - ] + expected = ( + [] + if pillow_installed + else [ + Error( + "Cannot use ImageField because Pillow is not installed.", + hint=( + "Get Pillow at https://pypi.org/project/Pillow/ " + 'or run command "python -m pip install Pillow".' + ), + obj=field, + id="fields.E210", + ), + ] + ) self.assertEqual(errors, expected) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class IntegerFieldTests(SimpleTestCase): - def test_max_length_warning(self): class Model(models.Model): integer = models.IntegerField(max_length=2) @@ -729,17 +828,21 @@ class IntegerFieldTests(SimpleTestCase): if field.auto_created: continue with self.subTest(name=field.name): - self.assertEqual(field.check(), [ - DjangoWarning( - "'max_length' is ignored when used with %s." % field.__class__.__name__, - hint="Remove 'max_length' from field", - obj=field, - id='fields.W122', - ) - ]) - - -@isolate_apps('invalid_models_tests') + self.assertEqual( + field.check(), + [ + DjangoWarning( + "'max_length' is ignored when used with %s." + % field.__class__.__name__, + hint="Remove 'max_length' from field", + obj=field, + id="fields.W122", + ) + ], + ) + + +@isolate_apps("invalid_models_tests") class TimeFieldTests(SimpleTestCase): maxDiff = None @@ -751,153 +854,171 @@ class TimeFieldTests(SimpleTestCase): field_tz = models.TimeField(default=now().timetz()) field_now = models.DateField(default=now) - names = ['field_dt', 'field_t', 'field_tz', 'field_now'] + names = ["field_dt", "field_t", "field_tz", "field_now"] fields = [Model._meta.get_field(name) for name in names] errors = [] for field in fields: errors.extend(field.check()) - self.assertEqual(errors, [ - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=fields[0], - id='fields.W161', - ), - DjangoWarning( - 'Fixed default value provided.', - hint='It seems you set a fixed date / time / datetime ' - 'value as default for this field. This may not be ' - 'what you want. If you want to have the current date ' - 'as default, use `django.utils.timezone.now`', - obj=fields[1], - id='fields.W161', - ), - DjangoWarning( - 'Fixed default value provided.', - hint=( - 'It seems you set a fixed date / time / datetime value as ' - 'default for this field. This may not be what you want. ' - 'If you want to have the current date as default, use ' - '`django.utils.timezone.now`' + self.assertEqual( + errors, + [ + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=fields[0], + id="fields.W161", + ), + DjangoWarning( + "Fixed default value provided.", + hint="It seems you set a fixed date / time / datetime " + "value as default for this field. This may not be " + "what you want. If you want to have the current date " + "as default, use `django.utils.timezone.now`", + obj=fields[1], + id="fields.W161", ), - obj=fields[2], - id='fields.W161', - ), - # field_now doesn't raise a warning. - ]) + DjangoWarning( + "Fixed default value provided.", + hint=( + "It seems you set a fixed date / time / datetime value as " + "default for this field. This may not be what you want. " + "If you want to have the current date as default, use " + "`django.utils.timezone.now`" + ), + obj=fields[2], + id="fields.W161", + ), + # field_now doesn't raise a warning. + ], + ) @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class TextFieldTests(TestCase): - - @skipIfDBFeature('supports_index_on_text_field') + @skipIfDBFeature("supports_index_on_text_field") def test_max_length_warning(self): class Model(models.Model): value = models.TextField(db_index=True) - field = Model._meta.get_field('value') + + field = Model._meta.get_field("value") field_type = field.db_type(connection) - self.assertEqual(field.check(databases=self.databases), [ - DjangoWarning( - '%s does not support a database index on %s columns.' - % (connection.display_name, field_type), - hint=( - "An index won't be created. Silence this warning if you " - "don't care about it." - ), - obj=field, - id='fields.W162', - ) - ]) + self.assertEqual( + field.check(databases=self.databases), + [ + DjangoWarning( + "%s does not support a database index on %s columns." + % (connection.display_name, field_type), + hint=( + "An index won't be created. Silence this warning if you " + "don't care about it." + ), + obj=field, + id="fields.W162", + ) + ], + ) def test_db_collation(self): class Model(models.Model): - field = models.TextField(db_collation='anything') + field = models.TextField(db_collation="anything") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") error = Error( - '%s does not support a database collation on TextFields.' + "%s does not support a database collation on TextFields." % connection.display_name, - id='fields.E190', + id="fields.E190", obj=field, ) - expected = [] if connection.features.supports_collation_on_textfield else [error] + expected = ( + [] if connection.features.supports_collation_on_textfield else [error] + ) self.assertEqual(field.check(databases=self.databases), expected) def test_db_collation_required_db_features(self): class Model(models.Model): - field = models.TextField(db_collation='anything') + field = models.TextField(db_collation="anything") class Meta: - required_db_features = {'supports_collation_on_textfield'} + required_db_features = {"supports_collation_on_textfield"} - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(databases=self.databases), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class UUIDFieldTests(TestCase): def test_choices_named_group(self): class Model(models.Model): field = models.UUIDField( choices=[ - ['knights', [ - [uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'), 'Lancelot'], - [uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2'), 'Galahad'], - ]], - [uuid.UUID('25d405be-4895-4d50-9b2e-d6695359ce47'), 'Other'], + [ + "knights", + [ + [ + uuid.UUID("5c859437-d061-4847-b3f7-e6b78852f8c8"), + "Lancelot", + ], + [ + uuid.UUID("c7853ec1-2ea3-4359-b02d-b54e8f1bcee2"), + "Galahad", + ], + ], + ], + [uuid.UUID("25d405be-4895-4d50-9b2e-d6695359ce47"), "Other"], ], ) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) -@isolate_apps('invalid_models_tests') -@skipUnlessDBFeature('supports_json_field') +@isolate_apps("invalid_models_tests") +@skipUnlessDBFeature("supports_json_field") class JSONFieldTests(TestCase): def test_invalid_default(self): class Model(models.Model): field = models.JSONField(default={}) - self.assertEqual(Model._meta.get_field('field').check(), [ - DjangoWarning( - msg=( - "JSONField default should be a callable instead of an " - "instance so that it's not shared between all field " - "instances." - ), - hint=( - 'Use a callable instead, e.g., use `dict` instead of `{}`.' - ), - obj=Model._meta.get_field('field'), - id='fields.E010', - ) - ]) + self.assertEqual( + Model._meta.get_field("field").check(), + [ + DjangoWarning( + msg=( + "JSONField default should be a callable instead of an " + "instance so that it's not shared between all field " + "instances." + ), + hint=("Use a callable instead, e.g., use `dict` instead of `{}`."), + obj=Model._meta.get_field("field"), + id="fields.E010", + ) + ], + ) def test_valid_default(self): class Model(models.Model): field = models.JSONField(default=dict) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_valid_default_none(self): class Model(models.Model): field = models.JSONField(default=None) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) def test_valid_callable_default(self): def callable_default(): - return {'it': 'works'} + return {"it": "works"} class Model(models.Model): field = models.JSONField(default=callable_default) - self.assertEqual(Model._meta.get_field('field').check(), []) + self.assertEqual(Model._meta.get_field("field").check(), []) diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py index 438d1b2a45..1dc4e7e6be 100644 --- a/tests/invalid_models_tests/test_relative_fields.py +++ b/tests/invalid_models_tests/test_relative_fields.py @@ -1,23 +1,23 @@ from unittest import mock -from django.core.checks import Error, Warning as DjangoWarning +from django.core.checks import Error +from django.core.checks import Warning as DjangoWarning from django.db import connection, models from django.test.testcases import SimpleTestCase from django.test.utils import isolate_apps, modify_settings, override_settings -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class RelativeFieldTests(SimpleTestCase): - def test_valid_foreign_key_without_accessor(self): class Target(models.Model): # There would be a clash if Model.field installed an accessor. model = models.IntegerField() class Model(models.Model): - field = models.ForeignKey(Target, models.CASCADE, related_name='+') + field = models.ForeignKey(Target, models.CASCADE, related_name="+") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) def test_foreign_key_to_missing_model(self): @@ -25,60 +25,68 @@ class RelativeFieldTests(SimpleTestCase): # test relative fields in isolation and we need to attach them to a # model. class Model(models.Model): - foreign_key = models.ForeignKey('Rel1', models.CASCADE) - - field = Model._meta.get_field('foreign_key') - self.assertEqual(field.check(), [ - Error( - "Field defines a relation with model 'Rel1', " - "which is either not installed, or is abstract.", - obj=field, - id='fields.E300', - ), - ]) + foreign_key = models.ForeignKey("Rel1", models.CASCADE) + + field = Model._meta.get_field("foreign_key") + self.assertEqual( + field.check(), + [ + Error( + "Field defines a relation with model 'Rel1', " + "which is either not installed, or is abstract.", + obj=field, + id="fields.E300", + ), + ], + ) - @isolate_apps('invalid_models_tests') + @isolate_apps("invalid_models_tests") def test_foreign_key_to_isolate_apps_model(self): """ #25723 - Referenced model registration lookup should be run against the field's model registry. """ + class OtherModel(models.Model): pass class Model(models.Model): - foreign_key = models.ForeignKey('OtherModel', models.CASCADE) + foreign_key = models.ForeignKey("OtherModel", models.CASCADE) - field = Model._meta.get_field('foreign_key') + field = Model._meta.get_field("foreign_key") self.assertEqual(field.check(from_model=Model), []) def test_many_to_many_to_missing_model(self): class Model(models.Model): m2m = models.ManyToManyField("Rel2") - field = Model._meta.get_field('m2m') - self.assertEqual(field.check(from_model=Model), [ - Error( - "Field defines a relation with model 'Rel2', " - "which is either not installed, or is abstract.", - obj=field, - id='fields.E300', - ), - ]) + field = Model._meta.get_field("m2m") + self.assertEqual( + field.check(from_model=Model), + [ + Error( + "Field defines a relation with model 'Rel2', " + "which is either not installed, or is abstract.", + obj=field, + id="fields.E300", + ), + ], + ) - @isolate_apps('invalid_models_tests') + @isolate_apps("invalid_models_tests") def test_many_to_many_to_isolate_apps_model(self): """ #25723 - Referenced model registration lookup should be run against the field's model registry. """ + class OtherModel(models.Model): pass class Model(models.Model): - m2m = models.ManyToManyField('OtherModel') + m2m = models.ManyToManyField("OtherModel") - field = Model._meta.get_field('m2m') + field = Model._meta.get_field("m2m") self.assertEqual(field.check(from_model=Model), []) def test_many_to_many_with_useless_options(self): @@ -88,93 +96,112 @@ class RelativeFieldTests(SimpleTestCase): class ModelM2M(models.Model): m2m = models.ManyToManyField(Model, null=True, validators=[lambda x: x]) - field = ModelM2M._meta.get_field('m2m') - self.assertEqual(ModelM2M.check(), [ - DjangoWarning( - 'null has no effect on ManyToManyField.', - obj=field, - id='fields.W340', - ), - DjangoWarning( - 'ManyToManyField does not support validators.', - obj=field, - id='fields.W341', - ), - ]) + field = ModelM2M._meta.get_field("m2m") + self.assertEqual( + ModelM2M.check(), + [ + DjangoWarning( + "null has no effect on ManyToManyField.", + obj=field, + id="fields.W340", + ), + DjangoWarning( + "ManyToManyField does not support validators.", + obj=field, + id="fields.W341", + ), + ], + ) def test_many_to_many_with_useless_related_name(self): class ModelM2M(models.Model): - m2m = models.ManyToManyField('self', related_name='children') - - field = ModelM2M._meta.get_field('m2m') - self.assertEqual(ModelM2M.check(), [ - DjangoWarning( - 'related_name has no effect on ManyToManyField with ' - 'a symmetrical relationship, e.g. to "self".', - obj=field, - id='fields.W345', - ), - ]) + m2m = models.ManyToManyField("self", related_name="children") + + field = ModelM2M._meta.get_field("m2m") + self.assertEqual( + ModelM2M.check(), + [ + DjangoWarning( + "related_name has no effect on ManyToManyField with " + 'a symmetrical relationship, e.g. to "self".', + obj=field, + id="fields.W345", + ), + ], + ) def test_ambiguous_relationship_model_from(self): class Person(models.Model): pass class Group(models.Model): - field = models.ManyToManyField('Person', through='AmbiguousRelationship') + field = models.ManyToManyField("Person", through="AmbiguousRelationship") class AmbiguousRelationship(models.Model): person = models.ForeignKey(Person, models.CASCADE) - first_group = models.ForeignKey(Group, models.CASCADE, related_name='first') - second_group = models.ForeignKey(Group, models.CASCADE, related_name='second') - - field = Group._meta.get_field('field') - self.assertEqual(field.check(from_model=Group), [ - Error( - "The model is used as an intermediate model by " - "'invalid_models_tests.Group.field', but it has more than one " - "foreign key from 'Group', which is ambiguous. You must " - "specify which foreign key Django should use via the " - "through_fields keyword argument.", - hint=( - 'If you want to create a recursive relationship, use ' - 'ManyToManyField("self", through="AmbiguousRelationship").' + first_group = models.ForeignKey(Group, models.CASCADE, related_name="first") + second_group = models.ForeignKey( + Group, models.CASCADE, related_name="second" + ) + + field = Group._meta.get_field("field") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Group.field', but it has more than one " + "foreign key from 'Group', which is ambiguous. You must " + "specify which foreign key Django should use via the " + "through_fields keyword argument.", + hint=( + "If you want to create a recursive relationship, use " + 'ManyToManyField("self", through="AmbiguousRelationship").' + ), + obj=field, + id="fields.E334", ), - obj=field, - id='fields.E334', - ), - ]) + ], + ) def test_ambiguous_relationship_model_to(self): - class Person(models.Model): pass class Group(models.Model): - field = models.ManyToManyField('Person', through="AmbiguousRelationship", related_name='tertiary') + field = models.ManyToManyField( + "Person", through="AmbiguousRelationship", related_name="tertiary" + ) class AmbiguousRelationship(models.Model): # Too much foreign keys to Person. - first_person = models.ForeignKey(Person, models.CASCADE, related_name="first") - second_person = models.ForeignKey(Person, models.CASCADE, related_name="second") + first_person = models.ForeignKey( + Person, models.CASCADE, related_name="first" + ) + second_person = models.ForeignKey( + Person, models.CASCADE, related_name="second" + ) second_model = models.ForeignKey(Group, models.CASCADE) - field = Group._meta.get_field('field') - self.assertEqual(field.check(from_model=Group), [ - Error( - "The model is used as an intermediate model by " - "'invalid_models_tests.Group.field', but it has more than one " - "foreign key to 'Person', which is ambiguous. You must specify " - "which foreign key Django should use via the through_fields " - "keyword argument.", - hint=( - 'If you want to create a recursive relationship, use ' - 'ManyToManyField("self", through="AmbiguousRelationship").' + field = Group._meta.get_field("field") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Group.field', but it has more than one " + "foreign key to 'Person', which is ambiguous. You must specify " + "which foreign key Django should use via the through_fields " + "keyword argument.", + hint=( + "If you want to create a recursive relationship, use " + 'ManyToManyField("self", through="AmbiguousRelationship").' + ), + obj=field, + id="fields.E335", ), - obj=field, - id='fields.E335', - ), - ]) + ], + ) def test_relationship_model_with_foreign_key_to_wrong_model(self): class WrongModel(models.Model): @@ -184,120 +211,144 @@ class RelativeFieldTests(SimpleTestCase): pass class Group(models.Model): - members = models.ManyToManyField('Person', through="InvalidRelationship") + members = models.ManyToManyField("Person", through="InvalidRelationship") class InvalidRelationship(models.Model): person = models.ForeignKey(Person, models.CASCADE) wrong_foreign_key = models.ForeignKey(WrongModel, models.CASCADE) # The last foreign key should point to Group model. - field = Group._meta.get_field('members') - self.assertEqual(field.check(from_model=Group), [ - Error( - "The model is used as an intermediate model by " - "'invalid_models_tests.Group.members', but it does not " - "have a foreign key to 'Group' or 'Person'.", - obj=InvalidRelationship, - id='fields.E336', - ), - ]) + field = Group._meta.get_field("members") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Group.members', but it does not " + "have a foreign key to 'Group' or 'Person'.", + obj=InvalidRelationship, + id="fields.E336", + ), + ], + ) def test_relationship_model_missing_foreign_key(self): class Person(models.Model): pass class Group(models.Model): - members = models.ManyToManyField('Person', through="InvalidRelationship") + members = models.ManyToManyField("Person", through="InvalidRelationship") class InvalidRelationship(models.Model): group = models.ForeignKey(Group, models.CASCADE) # No foreign key to Person - field = Group._meta.get_field('members') - self.assertEqual(field.check(from_model=Group), [ - Error( - "The model is used as an intermediate model by " - "'invalid_models_tests.Group.members', but it does not have " - "a foreign key to 'Group' or 'Person'.", - obj=InvalidRelationship, - id='fields.E336', - ), - ]) + field = Group._meta.get_field("members") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Group.members', but it does not have " + "a foreign key to 'Group' or 'Person'.", + obj=InvalidRelationship, + id="fields.E336", + ), + ], + ) def test_missing_relationship_model(self): class Person(models.Model): pass class Group(models.Model): - members = models.ManyToManyField('Person', through="MissingM2MModel") - - field = Group._meta.get_field('members') - self.assertEqual(field.check(from_model=Group), [ - Error( - "Field specifies a many-to-many relation through model " - "'MissingM2MModel', which has not been installed.", - obj=field, - id='fields.E331', - ), - ]) + members = models.ManyToManyField("Person", through="MissingM2MModel") + + field = Group._meta.get_field("members") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "Field specifies a many-to-many relation through model " + "'MissingM2MModel', which has not been installed.", + obj=field, + id="fields.E331", + ), + ], + ) def test_missing_relationship_model_on_model_check(self): class Person(models.Model): pass class Group(models.Model): - members = models.ManyToManyField('Person', through='MissingM2MModel') - - self.assertEqual(Group.check(), [ - Error( - "Field specifies a many-to-many relation through model " - "'MissingM2MModel', which has not been installed.", - obj=Group._meta.get_field('members'), - id='fields.E331', - ), - ]) + members = models.ManyToManyField("Person", through="MissingM2MModel") - @isolate_apps('invalid_models_tests') + self.assertEqual( + Group.check(), + [ + Error( + "Field specifies a many-to-many relation through model " + "'MissingM2MModel', which has not been installed.", + obj=Group._meta.get_field("members"), + id="fields.E331", + ), + ], + ) + + @isolate_apps("invalid_models_tests") def test_many_to_many_through_isolate_apps_model(self): """ #25723 - Through model registration lookup should be run against the field's model registry. """ + class GroupMember(models.Model): - person = models.ForeignKey('Person', models.CASCADE) - group = models.ForeignKey('Group', models.CASCADE) + person = models.ForeignKey("Person", models.CASCADE) + group = models.ForeignKey("Group", models.CASCADE) class Person(models.Model): pass class Group(models.Model): - members = models.ManyToManyField('Person', through='GroupMember') + members = models.ManyToManyField("Person", through="GroupMember") - field = Group._meta.get_field('members') + field = Group._meta.get_field("members") self.assertEqual(field.check(from_model=Group), []) def test_too_many_foreign_keys_in_self_referential_model(self): class Person(models.Model): - friends = models.ManyToManyField('self', through="InvalidRelationship", symmetrical=False) + friends = models.ManyToManyField( + "self", through="InvalidRelationship", symmetrical=False + ) class InvalidRelationship(models.Model): - first = models.ForeignKey(Person, models.CASCADE, related_name="rel_from_set_2") - second = models.ForeignKey(Person, models.CASCADE, related_name="rel_to_set_2") - third = models.ForeignKey(Person, models.CASCADE, related_name="too_many_by_far") - - field = Person._meta.get_field('friends') - self.assertEqual(field.check(from_model=Person), [ - Error( - "The model is used as an intermediate model by " - "'invalid_models_tests.Person.friends', but it has more than two " - "foreign keys to 'Person', which is ambiguous. You must specify " - "which two foreign keys Django should use via the through_fields " - "keyword argument.", - hint='Use through_fields to specify which two foreign keys Django should use.', - obj=InvalidRelationship, - id='fields.E333', - ), - ]) + first = models.ForeignKey( + Person, models.CASCADE, related_name="rel_from_set_2" + ) + second = models.ForeignKey( + Person, models.CASCADE, related_name="rel_to_set_2" + ) + third = models.ForeignKey( + Person, models.CASCADE, related_name="too_many_by_far" + ) + + field = Person._meta.get_field("friends") + self.assertEqual( + field.check(from_model=Person), + [ + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Person.friends', but it has more than two " + "foreign keys to 'Person', which is ambiguous. You must specify " + "which two foreign keys Django should use via the through_fields " + "keyword argument.", + hint="Use through_fields to specify which two foreign keys Django should use.", + obj=InvalidRelationship, + id="fields.E333", + ), + ], + ) def test_foreign_key_to_abstract_model(self): class AbstractModel(models.Model): @@ -305,17 +356,17 @@ class RelativeFieldTests(SimpleTestCase): abstract = True class Model(models.Model): - rel_string_foreign_key = models.ForeignKey('AbstractModel', models.CASCADE) + rel_string_foreign_key = models.ForeignKey("AbstractModel", models.CASCADE) rel_class_foreign_key = models.ForeignKey(AbstractModel, models.CASCADE) fields = [ - Model._meta.get_field('rel_string_foreign_key'), - Model._meta.get_field('rel_class_foreign_key'), + Model._meta.get_field("rel_string_foreign_key"), + Model._meta.get_field("rel_class_foreign_key"), ] expected_error = Error( "Field defines a relation with model 'AbstractModel', " "which is either not installed, or is abstract.", - id='fields.E300', + id="fields.E300", ) for field in fields: expected_error.obj = field @@ -327,17 +378,17 @@ class RelativeFieldTests(SimpleTestCase): abstract = True class Model(models.Model): - rel_string_m2m = models.ManyToManyField('AbstractModel') + rel_string_m2m = models.ManyToManyField("AbstractModel") rel_class_m2m = models.ManyToManyField(AbstractModel) fields = [ - Model._meta.get_field('rel_string_m2m'), - Model._meta.get_field('rel_class_m2m'), + Model._meta.get_field("rel_string_m2m"), + Model._meta.get_field("rel_class_m2m"), ] expected_error = Error( "Field defines a relation with model 'AbstractModel', " "which is either not installed, or is abstract.", - id='fields.E300', + id="fields.E300", ) for field in fields: expected_error.obj = field @@ -348,56 +399,65 @@ class RelativeFieldTests(SimpleTestCase): name = models.CharField(max_length=5) class Group(models.Model): - members = models.ManyToManyField('Person', unique=True) - - field = Group._meta.get_field('members') - self.assertEqual(field.check(from_model=Group), [ - Error( - 'ManyToManyFields cannot be unique.', - obj=field, - id='fields.E330', - ), - ]) + members = models.ManyToManyField("Person", unique=True) + + field = Group._meta.get_field("members") + self.assertEqual( + field.check(from_model=Group), + [ + Error( + "ManyToManyFields cannot be unique.", + obj=field, + id="fields.E330", + ), + ], + ) def test_foreign_key_to_non_unique_field(self): class Target(models.Model): bad = models.IntegerField() # No unique=True class Model(models.Model): - foreign_key = models.ForeignKey('Target', models.CASCADE, to_field='bad') - - field = Model._meta.get_field('foreign_key') - self.assertEqual(field.check(), [ - Error( - "'Target.bad' must be unique because it is referenced by a foreign key.", - hint=( - 'Add unique=True to this field or add a UniqueConstraint ' - '(without condition) in the model Meta.constraints.' + foreign_key = models.ForeignKey("Target", models.CASCADE, to_field="bad") + + field = Model._meta.get_field("foreign_key") + self.assertEqual( + field.check(), + [ + Error( + "'Target.bad' must be unique because it is referenced by a foreign key.", + hint=( + "Add unique=True to this field or add a UniqueConstraint " + "(without condition) in the model Meta.constraints." + ), + obj=field, + id="fields.E311", ), - obj=field, - id='fields.E311', - ), - ]) + ], + ) def test_foreign_key_to_non_unique_field_under_explicit_model(self): class Target(models.Model): bad = models.IntegerField() class Model(models.Model): - field = models.ForeignKey(Target, models.CASCADE, to_field='bad') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'Target.bad' must be unique because it is referenced by a foreign key.", - hint=( - 'Add unique=True to this field or add a UniqueConstraint ' - '(without condition) in the model Meta.constraints.' + field = models.ForeignKey(Target, models.CASCADE, to_field="bad") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'Target.bad' must be unique because it is referenced by a foreign key.", + hint=( + "Add unique=True to this field or add a UniqueConstraint " + "(without condition) in the model Meta.constraints." + ), + obj=field, + id="fields.E311", ), - obj=field, - id='fields.E311', - ), - ]) + ], + ) def test_foreign_key_to_partially_unique_field(self): class Target(models.Model): @@ -406,28 +466,31 @@ class RelativeFieldTests(SimpleTestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['source'], - name='tfktpuf_partial_unique', + fields=["source"], + name="tfktpuf_partial_unique", condition=models.Q(pk__gt=2), ), ] class Model(models.Model): - field = models.ForeignKey(Target, models.CASCADE, to_field='source') - - field = Model._meta.get_field('field') - self.assertEqual(field.check(), [ - Error( - "'Target.source' must be unique because it is referenced by a " - "foreign key.", - hint=( - 'Add unique=True to this field or add a UniqueConstraint ' - '(without condition) in the model Meta.constraints.' + field = models.ForeignKey(Target, models.CASCADE, to_field="source") + + field = Model._meta.get_field("field") + self.assertEqual( + field.check(), + [ + Error( + "'Target.source' must be unique because it is referenced by a " + "foreign key.", + hint=( + "Add unique=True to this field or add a UniqueConstraint " + "(without condition) in the model Meta.constraints." + ), + obj=field, + id="fields.E311", ), - obj=field, - id='fields.E311', - ), - ]) + ], + ) def test_foreign_key_to_unique_field_with_meta_constraint(self): class Target(models.Model): @@ -436,15 +499,15 @@ class RelativeFieldTests(SimpleTestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['source'], - name='tfktufwmc_unique', + fields=["source"], + name="tfktufwmc_unique", ), ] class Model(models.Model): - field = models.ForeignKey(Target, models.CASCADE, to_field='source') + field = models.ForeignKey(Target, models.CASCADE, to_field="source") - field = Model._meta.get_field('field') + field = Model._meta.get_field("field") self.assertEqual(field.check(), []) def test_foreign_object_to_non_unique_fields(self): @@ -460,24 +523,27 @@ class RelativeFieldTests(SimpleTestCase): person = models.ForeignObject( Person, on_delete=models.CASCADE, - from_fields=['person_country_id', 'person_city_id'], - to_fields=['country_id', 'city_id'], + from_fields=["person_country_id", "person_city_id"], + to_fields=["country_id", "city_id"], ) - field = MMembership._meta.get_field('person') - self.assertEqual(field.check(), [ - Error( - "No subset of the fields 'country_id', 'city_id' on model 'Person' is unique.", - hint=( - 'Mark a single field as unique=True or add a set of ' - 'fields to a unique constraint (via unique_together or a ' - 'UniqueConstraint (without condition) in the model ' - 'Meta.constraints).' - ), - obj=field, - id='fields.E310', - ) - ]) + field = MMembership._meta.get_field("person") + self.assertEqual( + field.check(), + [ + Error( + "No subset of the fields 'country_id', 'city_id' on model 'Person' is unique.", + hint=( + "Mark a single field as unique=True or add a set of " + "fields to a unique constraint (via unique_together or a " + "UniqueConstraint (without condition) in the model " + "Meta.constraints)." + ), + obj=field, + id="fields.E310", + ) + ], + ) def test_foreign_object_to_partially_unique_field(self): class Person(models.Model): @@ -487,8 +553,8 @@ class RelativeFieldTests(SimpleTestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['country_id', 'city_id'], - name='tfotpuf_partial_unique', + fields=["country_id", "city_id"], + name="tfotpuf_partial_unique", condition=models.Q(pk__gt=2), ), ] @@ -499,25 +565,28 @@ class RelativeFieldTests(SimpleTestCase): person = models.ForeignObject( Person, on_delete=models.CASCADE, - from_fields=['person_country_id', 'person_city_id'], - to_fields=['country_id', 'city_id'], + from_fields=["person_country_id", "person_city_id"], + to_fields=["country_id", "city_id"], ) - field = MMembership._meta.get_field('person') - self.assertEqual(field.check(), [ - Error( - "No subset of the fields 'country_id', 'city_id' on model " - "'Person' is unique.", - hint=( - 'Mark a single field as unique=True or add a set of ' - 'fields to a unique constraint (via unique_together or a ' - 'UniqueConstraint (without condition) in the model ' - 'Meta.constraints).' + field = MMembership._meta.get_field("person") + self.assertEqual( + field.check(), + [ + Error( + "No subset of the fields 'country_id', 'city_id' on model " + "'Person' is unique.", + hint=( + "Mark a single field as unique=True or add a set of " + "fields to a unique constraint (via unique_together or a " + "UniqueConstraint (without condition) in the model " + "Meta.constraints)." + ), + obj=field, + id="fields.E310", ), - obj=field, - id='fields.E310', - ), - ]) + ], + ) def test_foreign_object_to_unique_field_with_meta_constraint(self): class Person(models.Model): @@ -527,8 +596,8 @@ class RelativeFieldTests(SimpleTestCase): class Meta: constraints = [ models.UniqueConstraint( - fields=['country_id', 'city_id'], - name='tfotpuf_unique', + fields=["country_id", "city_id"], + name="tfotpuf_unique", ), ] @@ -538,11 +607,11 @@ class RelativeFieldTests(SimpleTestCase): person = models.ForeignObject( Person, on_delete=models.CASCADE, - from_fields=['person_country_id', 'person_city_id'], - to_fields=['country_id', 'city_id'], + from_fields=["person_country_id", "person_city_id"], + to_fields=["country_id", "city_id"], ) - field = MMembership._meta.get_field('person') + field = MMembership._meta.get_field("person") self.assertEqual(field.check(), []) def test_on_delete_set_null_on_non_nullable_field(self): @@ -550,125 +619,142 @@ class RelativeFieldTests(SimpleTestCase): pass class Model(models.Model): - foreign_key = models.ForeignKey('Person', models.SET_NULL) - - field = Model._meta.get_field('foreign_key') - self.assertEqual(field.check(), [ - Error( - 'Field specifies on_delete=SET_NULL, but cannot be null.', - hint='Set null=True argument on the field, or change the on_delete rule.', - obj=field, - id='fields.E320', - ), - ]) + foreign_key = models.ForeignKey("Person", models.SET_NULL) + + field = Model._meta.get_field("foreign_key") + self.assertEqual( + field.check(), + [ + Error( + "Field specifies on_delete=SET_NULL, but cannot be null.", + hint="Set null=True argument on the field, or change the on_delete rule.", + obj=field, + id="fields.E320", + ), + ], + ) def test_on_delete_set_default_without_default_value(self): class Person(models.Model): pass class Model(models.Model): - foreign_key = models.ForeignKey('Person', models.SET_DEFAULT) - - field = Model._meta.get_field('foreign_key') - self.assertEqual(field.check(), [ - Error( - 'Field specifies on_delete=SET_DEFAULT, but has no default value.', - hint='Set a default value, or change the on_delete rule.', - obj=field, - id='fields.E321', - ), - ]) + foreign_key = models.ForeignKey("Person", models.SET_DEFAULT) + + field = Model._meta.get_field("foreign_key") + self.assertEqual( + field.check(), + [ + Error( + "Field specifies on_delete=SET_DEFAULT, but has no default value.", + hint="Set a default value, or change the on_delete rule.", + obj=field, + id="fields.E321", + ), + ], + ) def test_nullable_primary_key(self): class Model(models.Model): field = models.IntegerField(primary_key=True, null=True) - field = Model._meta.get_field('field') - with mock.patch.object(connection.features, 'interprets_empty_strings_as_nulls', False): + field = Model._meta.get_field("field") + with mock.patch.object( + connection.features, "interprets_empty_strings_as_nulls", False + ): results = field.check() - self.assertEqual(results, [ - Error( - 'Primary keys must not have null=True.', - hint='Set null=False on the field, or remove primary_key=True argument.', - obj=field, - id='fields.E007', - ), - ]) + self.assertEqual( + results, + [ + Error( + "Primary keys must not have null=True.", + hint="Set null=False on the field, or remove primary_key=True argument.", + obj=field, + id="fields.E007", + ), + ], + ) def test_not_swapped_model(self): class SwappableModel(models.Model): # A model that can be, but isn't swapped out. References to this # model should *not* raise any validation error. class Meta: - swappable = 'TEST_SWAPPABLE_MODEL' + swappable = "TEST_SWAPPABLE_MODEL" class Model(models.Model): explicit_fk = models.ForeignKey( SwappableModel, models.CASCADE, - related_name='explicit_fk', + related_name="explicit_fk", ) implicit_fk = models.ForeignKey( - 'invalid_models_tests.SwappableModel', + "invalid_models_tests.SwappableModel", models.CASCADE, - related_name='implicit_fk', + related_name="implicit_fk", + ) + explicit_m2m = models.ManyToManyField( + SwappableModel, related_name="explicit_m2m" ) - explicit_m2m = models.ManyToManyField(SwappableModel, related_name='explicit_m2m') implicit_m2m = models.ManyToManyField( - 'invalid_models_tests.SwappableModel', - related_name='implicit_m2m', + "invalid_models_tests.SwappableModel", + related_name="implicit_m2m", ) - explicit_fk = Model._meta.get_field('explicit_fk') + explicit_fk = Model._meta.get_field("explicit_fk") self.assertEqual(explicit_fk.check(), []) - implicit_fk = Model._meta.get_field('implicit_fk') + implicit_fk = Model._meta.get_field("implicit_fk") self.assertEqual(implicit_fk.check(), []) - explicit_m2m = Model._meta.get_field('explicit_m2m') + explicit_m2m = Model._meta.get_field("explicit_m2m") self.assertEqual(explicit_m2m.check(from_model=Model), []) - implicit_m2m = Model._meta.get_field('implicit_m2m') + implicit_m2m = Model._meta.get_field("implicit_m2m") self.assertEqual(implicit_m2m.check(from_model=Model), []) - @override_settings(TEST_SWAPPED_MODEL='invalid_models_tests.Replacement') + @override_settings(TEST_SWAPPED_MODEL="invalid_models_tests.Replacement") def test_referencing_to_swapped_model(self): class Replacement(models.Model): pass class SwappedModel(models.Model): class Meta: - swappable = 'TEST_SWAPPED_MODEL' + swappable = "TEST_SWAPPED_MODEL" class Model(models.Model): explicit_fk = models.ForeignKey( SwappedModel, models.CASCADE, - related_name='explicit_fk', + related_name="explicit_fk", ) implicit_fk = models.ForeignKey( - 'invalid_models_tests.SwappedModel', + "invalid_models_tests.SwappedModel", models.CASCADE, - related_name='implicit_fk', + related_name="implicit_fk", + ) + explicit_m2m = models.ManyToManyField( + SwappedModel, related_name="explicit_m2m" ) - explicit_m2m = models.ManyToManyField(SwappedModel, related_name='explicit_m2m') implicit_m2m = models.ManyToManyField( - 'invalid_models_tests.SwappedModel', - related_name='implicit_m2m', + "invalid_models_tests.SwappedModel", + related_name="implicit_m2m", ) fields = [ - Model._meta.get_field('explicit_fk'), - Model._meta.get_field('implicit_fk'), - Model._meta.get_field('explicit_m2m'), - Model._meta.get_field('implicit_m2m'), + Model._meta.get_field("explicit_fk"), + Model._meta.get_field("implicit_fk"), + Model._meta.get_field("explicit_m2m"), + Model._meta.get_field("implicit_m2m"), ] expected_error = Error( - ("Field defines a relation with the model " - "'invalid_models_tests.SwappedModel', which has been swapped out."), + ( + "Field defines a relation with the model " + "'invalid_models_tests.SwappedModel', which has been swapped out." + ), hint="Update the relation to point at 'settings.TEST_SWAPPED_MODEL'.", - id='fields.E301', + id="fields.E301", ) for field in fields: @@ -677,68 +763,83 @@ class RelativeFieldTests(SimpleTestCase): def test_related_field_has_invalid_related_name(self): digit = 0 - illegal_non_alphanumeric = '!' - whitespace = '\t' + illegal_non_alphanumeric = "!" + whitespace = "\t" invalid_related_names = [ - '%s_begins_with_digit' % digit, - '%s_begins_with_illegal_non_alphanumeric' % illegal_non_alphanumeric, - '%s_begins_with_whitespace' % whitespace, - 'contains_%s_illegal_non_alphanumeric' % illegal_non_alphanumeric, - 'contains_%s_whitespace' % whitespace, - 'ends_with_with_illegal_non_alphanumeric_%s' % illegal_non_alphanumeric, - 'ends_with_whitespace_%s' % whitespace, - 'with', # a Python keyword - 'related_name\n', - '', - ',', # non-ASCII + "%s_begins_with_digit" % digit, + "%s_begins_with_illegal_non_alphanumeric" % illegal_non_alphanumeric, + "%s_begins_with_whitespace" % whitespace, + "contains_%s_illegal_non_alphanumeric" % illegal_non_alphanumeric, + "contains_%s_whitespace" % whitespace, + "ends_with_with_illegal_non_alphanumeric_%s" % illegal_non_alphanumeric, + "ends_with_whitespace_%s" % whitespace, + "with", # a Python keyword + "related_name\n", + "", + ",", # non-ASCII ] class Parent(models.Model): pass for invalid_related_name in invalid_related_names: - Child = type('Child%s' % invalid_related_name, (models.Model,), { - 'parent': models.ForeignKey('Parent', models.CASCADE, related_name=invalid_related_name), - '__module__': Parent.__module__, - }) + Child = type( + "Child%s" % invalid_related_name, + (models.Model,), + { + "parent": models.ForeignKey( + "Parent", models.CASCADE, related_name=invalid_related_name + ), + "__module__": Parent.__module__, + }, + ) - field = Child._meta.get_field('parent') - self.assertEqual(Child.check(), [ - Error( - "The name '%s' is invalid related_name for field Child%s.parent" - % (invalid_related_name, invalid_related_name), - hint="Related name must be a valid Python identifier or end with a '+'", - obj=field, - id='fields.E306', - ), - ]) + field = Child._meta.get_field("parent") + self.assertEqual( + Child.check(), + [ + Error( + "The name '%s' is invalid related_name for field Child%s.parent" + % (invalid_related_name, invalid_related_name), + hint="Related name must be a valid Python identifier or end with a '+'", + obj=field, + id="fields.E306", + ), + ], + ) def test_related_field_has_valid_related_name(self): - lowercase = 'a' - uppercase = 'A' + lowercase = "a" + uppercase = "A" digit = 0 related_names = [ - '%s_starts_with_lowercase' % lowercase, - '%s_tarts_with_uppercase' % uppercase, - '_starts_with_underscore', - 'contains_%s_digit' % digit, - 'ends_with_plus+', - '_+', - '+', - '試', - '試驗+', + "%s_starts_with_lowercase" % lowercase, + "%s_tarts_with_uppercase" % uppercase, + "_starts_with_underscore", + "contains_%s_digit" % digit, + "ends_with_plus+", + "_+", + "+", + "試", + "試驗+", ] class Parent(models.Model): pass for related_name in related_names: - Child = type('Child%s' % related_name, (models.Model,), { - 'parent': models.ForeignKey('Parent', models.CASCADE, related_name=related_name), - '__module__': Parent.__module__, - }) + Child = type( + "Child%s" % related_name, + (models.Model,), + { + "parent": models.ForeignKey( + "Parent", models.CASCADE, related_name=related_name + ), + "__module__": Parent.__module__, + }, + ) self.assertEqual(Child.check(), []) def test_to_fields_exist(self): @@ -751,104 +852,125 @@ class RelativeFieldTests(SimpleTestCase): parent = models.ForeignObject( Parent, on_delete=models.SET_NULL, - from_fields=('a', 'b'), - to_fields=('a', 'b'), + from_fields=("a", "b"), + to_fields=("a", "b"), ) - field = Child._meta.get_field('parent') - self.assertEqual(field.check(), [ - Error( - "The to_field 'a' doesn't exist on the related model 'invalid_models_tests.Parent'.", - obj=field, - id='fields.E312', - ), - Error( - "The to_field 'b' doesn't exist on the related model 'invalid_models_tests.Parent'.", - obj=field, - id='fields.E312', - ), - ]) + field = Child._meta.get_field("parent") + self.assertEqual( + field.check(), + [ + Error( + "The to_field 'a' doesn't exist on the related model 'invalid_models_tests.Parent'.", + obj=field, + id="fields.E312", + ), + Error( + "The to_field 'b' doesn't exist on the related model 'invalid_models_tests.Parent'.", + obj=field, + id="fields.E312", + ), + ], + ) def test_to_fields_not_checked_if_related_model_doesnt_exist(self): class Child(models.Model): a = models.PositiveIntegerField() b = models.PositiveIntegerField() parent = models.ForeignObject( - 'invalid_models_tests.Parent', + "invalid_models_tests.Parent", on_delete=models.SET_NULL, - from_fields=('a', 'b'), - to_fields=('a', 'b'), + from_fields=("a", "b"), + to_fields=("a", "b"), ) - field = Child._meta.get_field('parent') - self.assertEqual(field.check(), [ - Error( - "Field defines a relation with model 'invalid_models_tests.Parent', " - "which is either not installed, or is abstract.", - id='fields.E300', - obj=field, - ), - ]) + field = Child._meta.get_field("parent") + self.assertEqual( + field.check(), + [ + Error( + "Field defines a relation with model 'invalid_models_tests.Parent', " + "which is either not installed, or is abstract.", + id="fields.E300", + obj=field, + ), + ], + ) def test_invalid_related_query_name(self): class Target(models.Model): pass class Model(models.Model): - first = models.ForeignKey(Target, models.CASCADE, related_name='contains__double') - second = models.ForeignKey(Target, models.CASCADE, related_query_name='ends_underscore_') - - self.assertEqual(Model.check(), [ - Error( - "Reverse query name 'contains__double' must not contain '__'.", - hint=("Add or change a related_name or related_query_name " - "argument for this field."), - obj=Model._meta.get_field('first'), - id='fields.E309', - ), - Error( - "Reverse query name 'ends_underscore_' must not end with an " - "underscore.", - hint=("Add or change a related_name or related_query_name " - "argument for this field."), - obj=Model._meta.get_field('second'), - id='fields.E308', - ), - ]) + first = models.ForeignKey( + Target, models.CASCADE, related_name="contains__double" + ) + second = models.ForeignKey( + Target, models.CASCADE, related_query_name="ends_underscore_" + ) + self.assertEqual( + Model.check(), + [ + Error( + "Reverse query name 'contains__double' must not contain '__'.", + hint=( + "Add or change a related_name or related_query_name " + "argument for this field." + ), + obj=Model._meta.get_field("first"), + id="fields.E309", + ), + Error( + "Reverse query name 'ends_underscore_' must not end with an " + "underscore.", + hint=( + "Add or change a related_name or related_query_name " + "argument for this field." + ), + obj=Model._meta.get_field("second"), + id="fields.E308", + ), + ], + ) -@isolate_apps('invalid_models_tests') -class AccessorClashTests(SimpleTestCase): +@isolate_apps("invalid_models_tests") +class AccessorClashTests(SimpleTestCase): def test_fk_to_integer(self): self._test_accessor_clash( target=models.IntegerField(), - relative=models.ForeignKey('Target', models.CASCADE)) + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_fk_to_fk(self): self._test_accessor_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ForeignKey('Target', models.CASCADE)) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_fk_to_m2m(self): self._test_accessor_clash( - target=models.ManyToManyField('Another'), - relative=models.ForeignKey('Target', models.CASCADE)) + target=models.ManyToManyField("Another"), + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_m2m_to_integer(self): self._test_accessor_clash( - target=models.IntegerField(), - relative=models.ManyToManyField('Target')) + target=models.IntegerField(), relative=models.ManyToManyField("Target") + ) def test_m2m_to_fk(self): self._test_accessor_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ManyToManyField('Target')) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ManyToManyField("Target"), + ) def test_m2m_to_m2m(self): self._test_accessor_clash( - target=models.ManyToManyField('Another'), - relative=models.ManyToManyField('Target')) + target=models.ManyToManyField("Another"), + relative=models.ManyToManyField("Target"), + ) def _test_accessor_clash(self, target, relative): class Another(models.Model): @@ -860,20 +982,23 @@ class AccessorClashTests(SimpleTestCase): class Model(models.Model): rel = relative - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Target.model_set' for " - "'invalid_models_tests.Model.rel' clashes with field name " - "'invalid_models_tests.Target.model_set'.", - hint=( - "Rename field 'invalid_models_tests.Target.model_set', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.rel'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Target.model_set' for " + "'invalid_models_tests.Model.rel' clashes with field name " + "'invalid_models_tests.Target.model_set'.", + hint=( + "Rename field 'invalid_models_tests.Target.model_set', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.rel'." + ), + obj=Model._meta.get_field("rel"), + id="fields.E302", ), - obj=Model._meta.get_field('rel'), - id='fields.E302', - ), - ]) + ], + ) def test_clash_between_accessors(self): class Target(models.Model): @@ -883,111 +1008,123 @@ class AccessorClashTests(SimpleTestCase): foreign = models.ForeignKey(Target, models.CASCADE) m2m = models.ManyToManyField(Target) - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Target.model_set' for " - "'invalid_models_tests.Model.foreign' clashes with reverse " - "accessor for 'invalid_models_tests.Model.m2m'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.foreign' or " - "'invalid_models_tests.Model.m2m'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Target.model_set' for " + "'invalid_models_tests.Model.foreign' clashes with reverse " + "accessor for 'invalid_models_tests.Model.m2m'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.foreign' or " + "'invalid_models_tests.Model.m2m'." + ), + obj=Model._meta.get_field("foreign"), + id="fields.E304", ), - obj=Model._meta.get_field('foreign'), - id='fields.E304', - ), - Error( - "Reverse accessor 'Target.model_set' for " - "'invalid_models_tests.Model.m2m' clashes with reverse " - "accessor for 'invalid_models_tests.Model.foreign'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.m2m' or " - "'invalid_models_tests.Model.foreign'." + Error( + "Reverse accessor 'Target.model_set' for " + "'invalid_models_tests.Model.m2m' clashes with reverse " + "accessor for 'invalid_models_tests.Model.foreign'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.m2m' or " + "'invalid_models_tests.Model.foreign'." + ), + obj=Model._meta.get_field("m2m"), + id="fields.E304", ), - obj=Model._meta.get_field('m2m'), - id='fields.E304', - ), - ]) + ], + ) def test_m2m_to_m2m_with_inheritance(self): - """ Ref #22047. """ + """Ref #22047.""" class Target(models.Model): pass class Model(models.Model): - children = models.ManyToManyField('Child', related_name="m2m_clash", related_query_name="no_clash") + children = models.ManyToManyField( + "Child", related_name="m2m_clash", related_query_name="no_clash" + ) class Parent(models.Model): - m2m_clash = models.ManyToManyField('Target') + m2m_clash = models.ManyToManyField("Target") class Child(Parent): pass - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Child.m2m_clash' for " - "'invalid_models_tests.Model.children' clashes with field " - "name 'invalid_models_tests.Child.m2m_clash'.", - hint=( - "Rename field 'invalid_models_tests.Child.m2m_clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.children'." - ), - obj=Model._meta.get_field('children'), - id='fields.E302', - ) - ]) + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Child.m2m_clash' for " + "'invalid_models_tests.Model.children' clashes with field " + "name 'invalid_models_tests.Child.m2m_clash'.", + hint=( + "Rename field 'invalid_models_tests.Child.m2m_clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.children'." + ), + obj=Model._meta.get_field("children"), + id="fields.E302", + ) + ], + ) def test_no_clash_for_hidden_related_name(self): class Stub(models.Model): pass class ManyToManyRel(models.Model): - thing1 = models.ManyToManyField(Stub, related_name='+') - thing2 = models.ManyToManyField(Stub, related_name='+') + thing1 = models.ManyToManyField(Stub, related_name="+") + thing2 = models.ManyToManyField(Stub, related_name="+") class FKRel(models.Model): - thing1 = models.ForeignKey(Stub, models.CASCADE, related_name='+') - thing2 = models.ForeignKey(Stub, models.CASCADE, related_name='+') + thing1 = models.ForeignKey(Stub, models.CASCADE, related_name="+") + thing2 = models.ForeignKey(Stub, models.CASCADE, related_name="+") self.assertEqual(ManyToManyRel.check(), []) self.assertEqual(FKRel.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ReverseQueryNameClashTests(SimpleTestCase): - def test_fk_to_integer(self): self._test_reverse_query_name_clash( target=models.IntegerField(), - relative=models.ForeignKey('Target', models.CASCADE)) + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_fk_to_fk(self): self._test_reverse_query_name_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ForeignKey('Target', models.CASCADE)) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_fk_to_m2m(self): self._test_reverse_query_name_clash( - target=models.ManyToManyField('Another'), - relative=models.ForeignKey('Target', models.CASCADE)) + target=models.ManyToManyField("Another"), + relative=models.ForeignKey("Target", models.CASCADE), + ) def test_m2m_to_integer(self): self._test_reverse_query_name_clash( - target=models.IntegerField(), - relative=models.ManyToManyField('Target')) + target=models.IntegerField(), relative=models.ManyToManyField("Target") + ) def test_m2m_to_fk(self): self._test_reverse_query_name_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ManyToManyField('Target')) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ManyToManyField("Target"), + ) def test_m2m_to_m2m(self): self._test_reverse_query_name_clash( - target=models.ManyToManyField('Another'), - relative=models.ManyToManyField('Target')) + target=models.ManyToManyField("Another"), + relative=models.ManyToManyField("Target"), + ) def _test_reverse_query_name_clash(self, target, relative): class Another(models.Model): @@ -999,40 +1136,43 @@ class ReverseQueryNameClashTests(SimpleTestCase): class Model(models.Model): rel = relative - self.assertEqual(Model.check(), [ - Error( - "Reverse query name for 'invalid_models_tests.Model.rel' " - "clashes with field name 'invalid_models_tests.Target.model'.", - hint=( - "Rename field 'invalid_models_tests.Target.model', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.rel'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.model'.", + hint=( + "Rename field 'invalid_models_tests.Target.model', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.rel'." + ), + obj=Model._meta.get_field("rel"), + id="fields.E303", ), - obj=Model._meta.get_field('rel'), - id='fields.E303', - ), - ]) + ], + ) - @modify_settings(INSTALLED_APPS={'append': 'basic'}) - @isolate_apps('basic', 'invalid_models_tests') + @modify_settings(INSTALLED_APPS={"append": "basic"}) + @isolate_apps("basic", "invalid_models_tests") def test_no_clash_across_apps_without_accessor(self): class Target(models.Model): class Meta: - app_label = 'invalid_models_tests' + app_label = "invalid_models_tests" class Model(models.Model): - m2m = models.ManyToManyField(Target, related_name='+') + m2m = models.ManyToManyField(Target, related_name="+") class Meta: - app_label = 'basic' + app_label = "basic" def _test(): # Define model with the same name. class Model(models.Model): - m2m = models.ManyToManyField(Target, related_name='+') + m2m = models.ManyToManyField(Target, related_name="+") class Meta: - app_label = 'invalid_models_tests' + app_label = "invalid_models_tests" self.assertEqual(Model.check(), []) @@ -1040,38 +1180,43 @@ class ReverseQueryNameClashTests(SimpleTestCase): self.assertEqual(Model.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ExplicitRelatedNameClashTests(SimpleTestCase): - def test_fk_to_integer(self): self._test_explicit_related_name_clash( target=models.IntegerField(), - relative=models.ForeignKey('Target', models.CASCADE, related_name='clash')) + relative=models.ForeignKey("Target", models.CASCADE, related_name="clash"), + ) def test_fk_to_fk(self): self._test_explicit_related_name_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ForeignKey('Target', models.CASCADE, related_name='clash')) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ForeignKey("Target", models.CASCADE, related_name="clash"), + ) def test_fk_to_m2m(self): self._test_explicit_related_name_clash( - target=models.ManyToManyField('Another'), - relative=models.ForeignKey('Target', models.CASCADE, related_name='clash')) + target=models.ManyToManyField("Another"), + relative=models.ForeignKey("Target", models.CASCADE, related_name="clash"), + ) def test_m2m_to_integer(self): self._test_explicit_related_name_clash( target=models.IntegerField(), - relative=models.ManyToManyField('Target', related_name='clash')) + relative=models.ManyToManyField("Target", related_name="clash"), + ) def test_m2m_to_fk(self): self._test_explicit_related_name_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ManyToManyField('Target', related_name='clash')) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ManyToManyField("Target", related_name="clash"), + ) def test_m2m_to_m2m(self): self._test_explicit_related_name_clash( - target=models.ManyToManyField('Another'), - relative=models.ManyToManyField('Target', related_name='clash')) + target=models.ManyToManyField("Another"), + relative=models.ManyToManyField("Target", related_name="clash"), + ) def _test_explicit_related_name_clash(self, target, relative): class Another(models.Model): @@ -1083,106 +1228,114 @@ class ExplicitRelatedNameClashTests(SimpleTestCase): class Model(models.Model): rel = relative - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Target.clash' for " - "'invalid_models_tests.Model.rel' clashes with field name " - "'invalid_models_tests.Target.clash'.", - hint=( - "Rename field 'invalid_models_tests.Target.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.rel'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Target.clash' for " + "'invalid_models_tests.Model.rel' clashes with field name " + "'invalid_models_tests.Target.clash'.", + hint=( + "Rename field 'invalid_models_tests.Target.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.rel'." + ), + obj=Model._meta.get_field("rel"), + id="fields.E302", ), - obj=Model._meta.get_field('rel'), - id='fields.E302', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.rel' " - "clashes with field name 'invalid_models_tests.Target.clash'.", - hint=( - "Rename field 'invalid_models_tests.Target.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.rel'." + Error( + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.clash'.", + hint=( + "Rename field 'invalid_models_tests.Target.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.rel'." + ), + obj=Model._meta.get_field("rel"), + id="fields.E303", ), - obj=Model._meta.get_field('rel'), - id='fields.E303', - ), - ]) + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ExplicitRelatedQueryNameClashTests(SimpleTestCase): - def test_fk_to_integer(self, related_name=None): self._test_explicit_related_query_name_clash( target=models.IntegerField(), relative=models.ForeignKey( - 'Target', + "Target", models.CASCADE, related_name=related_name, - related_query_name='clash', - ) + related_query_name="clash", + ), ) def test_hidden_fk_to_integer(self, related_name=None): - self.test_fk_to_integer(related_name='+') + self.test_fk_to_integer(related_name="+") def test_fk_to_fk(self, related_name=None): self._test_explicit_related_query_name_clash( - target=models.ForeignKey('Another', models.CASCADE), + target=models.ForeignKey("Another", models.CASCADE), relative=models.ForeignKey( - 'Target', + "Target", models.CASCADE, related_name=related_name, - related_query_name='clash', - ) + related_query_name="clash", + ), ) def test_hidden_fk_to_fk(self): - self.test_fk_to_fk(related_name='+') + self.test_fk_to_fk(related_name="+") def test_fk_to_m2m(self, related_name=None): self._test_explicit_related_query_name_clash( - target=models.ManyToManyField('Another'), + target=models.ManyToManyField("Another"), relative=models.ForeignKey( - 'Target', + "Target", models.CASCADE, related_name=related_name, - related_query_name='clash', - ) + related_query_name="clash", + ), ) def test_hidden_fk_to_m2m(self): - self.test_fk_to_m2m(related_name='+') + self.test_fk_to_m2m(related_name="+") def test_m2m_to_integer(self, related_name=None): self._test_explicit_related_query_name_clash( target=models.IntegerField(), - relative=models.ManyToManyField('Target', related_name=related_name, related_query_name='clash')) + relative=models.ManyToManyField( + "Target", related_name=related_name, related_query_name="clash" + ), + ) def test_hidden_m2m_to_integer(self): - self.test_m2m_to_integer(related_name='+') + self.test_m2m_to_integer(related_name="+") def test_m2m_to_fk(self, related_name=None): self._test_explicit_related_query_name_clash( - target=models.ForeignKey('Another', models.CASCADE), - relative=models.ManyToManyField('Target', related_name=related_name, related_query_name='clash')) + target=models.ForeignKey("Another", models.CASCADE), + relative=models.ManyToManyField( + "Target", related_name=related_name, related_query_name="clash" + ), + ) def test_hidden_m2m_to_fk(self): - self.test_m2m_to_fk(related_name='+') + self.test_m2m_to_fk(related_name="+") def test_m2m_to_m2m(self, related_name=None): self._test_explicit_related_query_name_clash( - target=models.ManyToManyField('Another'), + target=models.ManyToManyField("Another"), relative=models.ManyToManyField( - 'Target', + "Target", related_name=related_name, - related_query_name='clash', - ) + related_query_name="clash", + ), ) def test_hidden_m2m_to_m2m(self): - self.test_m2m_to_m2m(related_name='+') + self.test_m2m_to_m2m(related_name="+") def _test_explicit_related_query_name_clash(self, target, relative): class Another(models.Model): @@ -1194,205 +1347,233 @@ class ExplicitRelatedQueryNameClashTests(SimpleTestCase): class Model(models.Model): rel = relative - self.assertEqual(Model.check(), [ - Error( - "Reverse query name for 'invalid_models_tests.Model.rel' " - "clashes with field name 'invalid_models_tests.Target.clash'.", - hint=( - "Rename field 'invalid_models_tests.Target.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.rel'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.clash'.", + hint=( + "Rename field 'invalid_models_tests.Target.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.rel'." + ), + obj=Model._meta.get_field("rel"), + id="fields.E303", ), - obj=Model._meta.get_field('rel'), - id='fields.E303', - ), - ]) + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class SelfReferentialM2MClashTests(SimpleTestCase): - def test_clash_between_accessors(self): class Model(models.Model): - first_m2m = models.ManyToManyField('self', symmetrical=False) - second_m2m = models.ManyToManyField('self', symmetrical=False) - - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Model.model_set' for " - "'invalid_models_tests.Model.first_m2m' clashes with reverse " - "accessor for 'invalid_models_tests.Model.second_m2m'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.first_m2m' or " - "'invalid_models_tests.Model.second_m2m'." + first_m2m = models.ManyToManyField("self", symmetrical=False) + second_m2m = models.ManyToManyField("self", symmetrical=False) + + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Model.model_set' for " + "'invalid_models_tests.Model.first_m2m' clashes with reverse " + "accessor for 'invalid_models_tests.Model.second_m2m'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.first_m2m' or " + "'invalid_models_tests.Model.second_m2m'." + ), + obj=Model._meta.get_field("first_m2m"), + id="fields.E304", ), - obj=Model._meta.get_field('first_m2m'), - id='fields.E304', - ), - Error( - "Reverse accessor 'Model.model_set' for " - "'invalid_models_tests.Model.second_m2m' clashes with reverse " - "accessor for 'invalid_models_tests.Model.first_m2m'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.second_m2m' or " - "'invalid_models_tests.Model.first_m2m'." + Error( + "Reverse accessor 'Model.model_set' for " + "'invalid_models_tests.Model.second_m2m' clashes with reverse " + "accessor for 'invalid_models_tests.Model.first_m2m'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.second_m2m' or " + "'invalid_models_tests.Model.first_m2m'." + ), + obj=Model._meta.get_field("second_m2m"), + id="fields.E304", ), - obj=Model._meta.get_field('second_m2m'), - id='fields.E304', - ), - ]) + ], + ) def test_accessor_clash(self): class Model(models.Model): model_set = models.ManyToManyField("self", symmetrical=False) - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Model.model_set' for " - "'invalid_models_tests.Model.model_set' clashes with field " - "name 'invalid_models_tests.Model.model_set'.", - hint=( - "Rename field 'invalid_models_tests.Model.model_set', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.model_set'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Model.model_set' for " + "'invalid_models_tests.Model.model_set' clashes with field " + "name 'invalid_models_tests.Model.model_set'.", + hint=( + "Rename field 'invalid_models_tests.Model.model_set', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.model_set'." + ), + obj=Model._meta.get_field("model_set"), + id="fields.E302", ), - obj=Model._meta.get_field('model_set'), - id='fields.E302', - ), - ]) + ], + ) def test_reverse_query_name_clash(self): class Model(models.Model): model = models.ManyToManyField("self", symmetrical=False) - self.assertEqual(Model.check(), [ - Error( - "Reverse query name for 'invalid_models_tests.Model.model' " - "clashes with field name 'invalid_models_tests.Model.model'.", - hint=( - "Rename field 'invalid_models_tests.Model.model', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.model'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse query name for 'invalid_models_tests.Model.model' " + "clashes with field name 'invalid_models_tests.Model.model'.", + hint=( + "Rename field 'invalid_models_tests.Model.model', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.model'." + ), + obj=Model._meta.get_field("model"), + id="fields.E303", ), - obj=Model._meta.get_field('model'), - id='fields.E303', - ), - ]) + ], + ) def test_clash_under_explicit_related_name(self): class Model(models.Model): clash = models.IntegerField() - m2m = models.ManyToManyField("self", symmetrical=False, related_name='clash') - - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Model.clash' for " - "'invalid_models_tests.Model.m2m' clashes with field name " - "'invalid_models_tests.Model.clash'.", - hint=( - "Rename field 'invalid_models_tests.Model.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.m2m'." + m2m = models.ManyToManyField( + "self", symmetrical=False, related_name="clash" + ) + + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Model.clash' for " + "'invalid_models_tests.Model.m2m' clashes with field name " + "'invalid_models_tests.Model.clash'.", + hint=( + "Rename field 'invalid_models_tests.Model.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.m2m'." + ), + obj=Model._meta.get_field("m2m"), + id="fields.E302", ), - obj=Model._meta.get_field('m2m'), - id='fields.E302', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.m2m' " - "clashes with field name 'invalid_models_tests.Model.clash'.", - hint=( - "Rename field 'invalid_models_tests.Model.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.m2m'." + Error( + "Reverse query name for 'invalid_models_tests.Model.m2m' " + "clashes with field name 'invalid_models_tests.Model.clash'.", + hint=( + "Rename field 'invalid_models_tests.Model.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.m2m'." + ), + obj=Model._meta.get_field("m2m"), + id="fields.E303", ), - obj=Model._meta.get_field('m2m'), - id='fields.E303', - ), - ]) + ], + ) def test_valid_model(self): class Model(models.Model): - first = models.ManyToManyField("self", symmetrical=False, related_name='first_accessor') - second = models.ManyToManyField("self", symmetrical=False, related_name='second_accessor') + first = models.ManyToManyField( + "self", symmetrical=False, related_name="first_accessor" + ) + second = models.ManyToManyField( + "self", symmetrical=False, related_name="second_accessor" + ) self.assertEqual(Model.check(), []) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class SelfReferentialFKClashTests(SimpleTestCase): - def test_accessor_clash(self): class Model(models.Model): model_set = models.ForeignKey("Model", models.CASCADE) - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Model.model_set' for " - "'invalid_models_tests.Model.model_set' clashes with field " - "name 'invalid_models_tests.Model.model_set'.", - hint=( - "Rename field 'invalid_models_tests.Model.model_set', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.model_set'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Model.model_set' for " + "'invalid_models_tests.Model.model_set' clashes with field " + "name 'invalid_models_tests.Model.model_set'.", + hint=( + "Rename field 'invalid_models_tests.Model.model_set', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.model_set'." + ), + obj=Model._meta.get_field("model_set"), + id="fields.E302", ), - obj=Model._meta.get_field('model_set'), - id='fields.E302', - ), - ]) + ], + ) def test_reverse_query_name_clash(self): class Model(models.Model): model = models.ForeignKey("Model", models.CASCADE) - self.assertEqual(Model.check(), [ - Error( - "Reverse query name for 'invalid_models_tests.Model.model' " - "clashes with field name 'invalid_models_tests.Model.model'.", - hint=( - "Rename field 'invalid_models_tests.Model.model', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.model'." + self.assertEqual( + Model.check(), + [ + Error( + "Reverse query name for 'invalid_models_tests.Model.model' " + "clashes with field name 'invalid_models_tests.Model.model'.", + hint=( + "Rename field 'invalid_models_tests.Model.model', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.model'." + ), + obj=Model._meta.get_field("model"), + id="fields.E303", ), - obj=Model._meta.get_field('model'), - id='fields.E303', - ), - ]) + ], + ) def test_clash_under_explicit_related_name(self): class Model(models.Model): clash = models.CharField(max_length=10) - foreign = models.ForeignKey("Model", models.CASCADE, related_name='clash') - - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Model.clash' for " - "'invalid_models_tests.Model.foreign' clashes with field name " - "'invalid_models_tests.Model.clash'.", - hint=( - "Rename field 'invalid_models_tests.Model.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.foreign'." + foreign = models.ForeignKey("Model", models.CASCADE, related_name="clash") + + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Model.clash' for " + "'invalid_models_tests.Model.foreign' clashes with field name " + "'invalid_models_tests.Model.clash'.", + hint=( + "Rename field 'invalid_models_tests.Model.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.foreign'." + ), + obj=Model._meta.get_field("foreign"), + id="fields.E302", ), - obj=Model._meta.get_field('foreign'), - id='fields.E302', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.foreign' " - "clashes with field name 'invalid_models_tests.Model.clash'.", - hint=( - "Rename field 'invalid_models_tests.Model.clash', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.foreign'." + Error( + "Reverse query name for 'invalid_models_tests.Model.foreign' " + "clashes with field name 'invalid_models_tests.Model.clash'.", + hint=( + "Rename field 'invalid_models_tests.Model.clash', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.foreign'." + ), + obj=Model._meta.get_field("foreign"), + id="fields.E303", ), - obj=Model._meta.get_field('foreign'), - id='fields.E303', - ), - ]) + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class ComplexClashTests(SimpleTestCase): # New tests should not be included here, because this is a single, @@ -1408,158 +1589,161 @@ class ComplexClashTests(SimpleTestCase): class Model(models.Model): src_safe = models.CharField(max_length=10) - foreign_1 = models.ForeignKey(Target, models.CASCADE, related_name='id') - foreign_2 = models.ForeignKey(Target, models.CASCADE, related_name='src_safe') - - m2m_1 = models.ManyToManyField(Target, related_name='id') - m2m_2 = models.ManyToManyField(Target, related_name='src_safe') - - self.assertEqual(Model.check(), [ - Error( - "Reverse accessor 'Target.id' for " - "'invalid_models_tests.Model.foreign_1' clashes with field " - "name 'invalid_models_tests.Target.id'.", - hint=( - "Rename field 'invalid_models_tests.Target.id', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.foreign_1'." + foreign_1 = models.ForeignKey(Target, models.CASCADE, related_name="id") + foreign_2 = models.ForeignKey( + Target, models.CASCADE, related_name="src_safe" + ) + + m2m_1 = models.ManyToManyField(Target, related_name="id") + m2m_2 = models.ManyToManyField(Target, related_name="src_safe") + + self.assertEqual( + Model.check(), + [ + Error( + "Reverse accessor 'Target.id' for " + "'invalid_models_tests.Model.foreign_1' clashes with field " + "name 'invalid_models_tests.Target.id'.", + hint=( + "Rename field 'invalid_models_tests.Target.id', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.foreign_1'." + ), + obj=Model._meta.get_field("foreign_1"), + id="fields.E302", ), - obj=Model._meta.get_field('foreign_1'), - id='fields.E302', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.foreign_1' " - "clashes with field name 'invalid_models_tests.Target.id'.", - hint=( - "Rename field 'invalid_models_tests.Target.id', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.foreign_1'." + Error( + "Reverse query name for 'invalid_models_tests.Model.foreign_1' " + "clashes with field name 'invalid_models_tests.Target.id'.", + hint=( + "Rename field 'invalid_models_tests.Target.id', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.foreign_1'." + ), + obj=Model._meta.get_field("foreign_1"), + id="fields.E303", ), - obj=Model._meta.get_field('foreign_1'), - id='fields.E303', - ), - Error( - "Reverse accessor 'Target.id' for " - "'invalid_models_tests.Model.foreign_1' clashes with reverse " - "accessor for 'invalid_models_tests.Model.m2m_1'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.foreign_1' or " - "'invalid_models_tests.Model.m2m_1'." + Error( + "Reverse accessor 'Target.id' for " + "'invalid_models_tests.Model.foreign_1' clashes with reverse " + "accessor for 'invalid_models_tests.Model.m2m_1'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.foreign_1' or " + "'invalid_models_tests.Model.m2m_1'." + ), + obj=Model._meta.get_field("foreign_1"), + id="fields.E304", ), - obj=Model._meta.get_field('foreign_1'), - id='fields.E304', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.foreign_1' " - "clashes with reverse query name for " - "'invalid_models_tests.Model.m2m_1'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.foreign_1' or " - "'invalid_models_tests.Model.m2m_1'." + Error( + "Reverse query name for 'invalid_models_tests.Model.foreign_1' " + "clashes with reverse query name for " + "'invalid_models_tests.Model.m2m_1'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.foreign_1' or " + "'invalid_models_tests.Model.m2m_1'." + ), + obj=Model._meta.get_field("foreign_1"), + id="fields.E305", ), - obj=Model._meta.get_field('foreign_1'), - id='fields.E305', - ), - - Error( - "Reverse accessor 'Target.src_safe' for " - "'invalid_models_tests.Model.foreign_2' clashes with reverse " - "accessor for 'invalid_models_tests.Model.m2m_2'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.foreign_2' or " - "'invalid_models_tests.Model.m2m_2'." + Error( + "Reverse accessor 'Target.src_safe' for " + "'invalid_models_tests.Model.foreign_2' clashes with reverse " + "accessor for 'invalid_models_tests.Model.m2m_2'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.foreign_2' or " + "'invalid_models_tests.Model.m2m_2'." + ), + obj=Model._meta.get_field("foreign_2"), + id="fields.E304", ), - obj=Model._meta.get_field('foreign_2'), - id='fields.E304', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.foreign_2' " - "clashes with reverse query name for " - "'invalid_models_tests.Model.m2m_2'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.foreign_2' or " - "'invalid_models_tests.Model.m2m_2'." + Error( + "Reverse query name for 'invalid_models_tests.Model.foreign_2' " + "clashes with reverse query name for " + "'invalid_models_tests.Model.m2m_2'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.foreign_2' or " + "'invalid_models_tests.Model.m2m_2'." + ), + obj=Model._meta.get_field("foreign_2"), + id="fields.E305", ), - obj=Model._meta.get_field('foreign_2'), - id='fields.E305', - ), - - Error( - "Reverse accessor 'Target.id' for " - "'invalid_models_tests.Model.m2m_1' clashes with field name " - "'invalid_models_tests.Target.id'.", - hint=( - "Rename field 'invalid_models_tests.Target.id', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.m2m_1'." + Error( + "Reverse accessor 'Target.id' for " + "'invalid_models_tests.Model.m2m_1' clashes with field name " + "'invalid_models_tests.Target.id'.", + hint=( + "Rename field 'invalid_models_tests.Target.id', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.m2m_1'." + ), + obj=Model._meta.get_field("m2m_1"), + id="fields.E302", ), - obj=Model._meta.get_field('m2m_1'), - id='fields.E302', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.m2m_1' " - "clashes with field name 'invalid_models_tests.Target.id'.", - hint=( - "Rename field 'invalid_models_tests.Target.id', or " - "add/change a related_name argument to the definition for " - "field 'invalid_models_tests.Model.m2m_1'." + Error( + "Reverse query name for 'invalid_models_tests.Model.m2m_1' " + "clashes with field name 'invalid_models_tests.Target.id'.", + hint=( + "Rename field 'invalid_models_tests.Target.id', or " + "add/change a related_name argument to the definition for " + "field 'invalid_models_tests.Model.m2m_1'." + ), + obj=Model._meta.get_field("m2m_1"), + id="fields.E303", ), - obj=Model._meta.get_field('m2m_1'), - id='fields.E303', - ), - Error( - "Reverse accessor 'Target.id' for " - "'invalid_models_tests.Model.m2m_1' clashes with reverse " - "accessor for 'invalid_models_tests.Model.foreign_1'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.m2m_1' or " - "'invalid_models_tests.Model.foreign_1'." + Error( + "Reverse accessor 'Target.id' for " + "'invalid_models_tests.Model.m2m_1' clashes with reverse " + "accessor for 'invalid_models_tests.Model.foreign_1'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.m2m_1' or " + "'invalid_models_tests.Model.foreign_1'." + ), + obj=Model._meta.get_field("m2m_1"), + id="fields.E304", ), - obj=Model._meta.get_field('m2m_1'), - id='fields.E304', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.m2m_1' " - "clashes with reverse query name for " - "'invalid_models_tests.Model.foreign_1'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.m2m_1' or " - "'invalid_models_tests.Model.foreign_1'." + Error( + "Reverse query name for 'invalid_models_tests.Model.m2m_1' " + "clashes with reverse query name for " + "'invalid_models_tests.Model.foreign_1'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.m2m_1' or " + "'invalid_models_tests.Model.foreign_1'." + ), + obj=Model._meta.get_field("m2m_1"), + id="fields.E305", ), - obj=Model._meta.get_field('m2m_1'), - id='fields.E305', - ), - Error( - "Reverse accessor 'Target.src_safe' for " - "'invalid_models_tests.Model.m2m_2' clashes with reverse " - "accessor for 'invalid_models_tests.Model.foreign_2'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.m2m_2' or " - "'invalid_models_tests.Model.foreign_2'." + Error( + "Reverse accessor 'Target.src_safe' for " + "'invalid_models_tests.Model.m2m_2' clashes with reverse " + "accessor for 'invalid_models_tests.Model.foreign_2'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.m2m_2' or " + "'invalid_models_tests.Model.foreign_2'." + ), + obj=Model._meta.get_field("m2m_2"), + id="fields.E304", ), - obj=Model._meta.get_field('m2m_2'), - id='fields.E304', - ), - Error( - "Reverse query name for 'invalid_models_tests.Model.m2m_2' " - "clashes with reverse query name for " - "'invalid_models_tests.Model.foreign_2'.", - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Model.m2m_2' or " - "'invalid_models_tests.Model.foreign_2'." + Error( + "Reverse query name for 'invalid_models_tests.Model.m2m_2' " + "clashes with reverse query name for " + "'invalid_models_tests.Model.foreign_2'.", + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Model.m2m_2' or " + "'invalid_models_tests.Model.foreign_2'." + ), + obj=Model._meta.get_field("m2m_2"), + id="fields.E305", ), - obj=Model._meta.get_field('m2m_2'), - id='fields.E305', - ), - ]) + ], + ) def test_clash_parent_link(self): class Parent(models.Model): @@ -1569,136 +1753,170 @@ class ComplexClashTests(SimpleTestCase): other_parent = models.OneToOneField(Parent, models.CASCADE) errors = [ - ('fields.E304', 'accessor', " 'Parent.child'", 'parent_ptr', 'other_parent'), - ('fields.E305', 'query name', '', 'parent_ptr', 'other_parent'), - ('fields.E304', 'accessor', " 'Parent.child'", 'other_parent', 'parent_ptr'), - ('fields.E305', 'query name', '', 'other_parent', 'parent_ptr'), + ( + "fields.E304", + "accessor", + " 'Parent.child'", + "parent_ptr", + "other_parent", + ), + ("fields.E305", "query name", "", "parent_ptr", "other_parent"), + ( + "fields.E304", + "accessor", + " 'Parent.child'", + "other_parent", + "parent_ptr", + ), + ("fields.E305", "query name", "", "other_parent", "parent_ptr"), ] - self.assertEqual(Child.check(), [ - Error( - "Reverse %s%s for 'invalid_models_tests.Child.%s' clashes with " - "reverse %s for 'invalid_models_tests.Child.%s'." - % (attr, rel_name, field_name, attr, clash_name), - hint=( - "Add or change a related_name argument to the definition " - "for 'invalid_models_tests.Child.%s' or " - "'invalid_models_tests.Child.%s'." % (field_name, clash_name) - ), - obj=Child._meta.get_field(field_name), - id=error_id, - ) - for error_id, attr, rel_name, field_name, clash_name in errors - ]) + self.assertEqual( + Child.check(), + [ + Error( + "Reverse %s%s for 'invalid_models_tests.Child.%s' clashes with " + "reverse %s for 'invalid_models_tests.Child.%s'." + % (attr, rel_name, field_name, attr, clash_name), + hint=( + "Add or change a related_name argument to the definition " + "for 'invalid_models_tests.Child.%s' or " + "'invalid_models_tests.Child.%s'." % (field_name, clash_name) + ), + obj=Child._meta.get_field(field_name), + id=error_id, + ) + for error_id, attr, rel_name, field_name, clash_name in errors + ], + ) -@isolate_apps('invalid_models_tests') +@isolate_apps("invalid_models_tests") class M2mThroughFieldsTests(SimpleTestCase): def test_m2m_field_argument_validation(self): """ ManyToManyField accepts the ``through_fields`` kwarg only if an intermediary table is specified. """ + class Fan(models.Model): pass - with self.assertRaisesMessage(ValueError, 'Cannot specify through_fields without a through model'): - models.ManyToManyField(Fan, through_fields=('f1', 'f2')) + with self.assertRaisesMessage( + ValueError, "Cannot specify through_fields without a through model" + ): + models.ManyToManyField(Fan, through_fields=("f1", "f2")) def test_invalid_order(self): """ Mixing up the order of link fields to ManyToManyField.through_fields triggers validation errors. """ + class Fan(models.Model): pass class Event(models.Model): - invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invitee', 'event')) + invitees = models.ManyToManyField( + Fan, through="Invitation", through_fields=("invitee", "event") + ) class Invitation(models.Model): event = models.ForeignKey(Event, models.CASCADE) invitee = models.ForeignKey(Fan, models.CASCADE) - inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+') - - field = Event._meta.get_field('invitees') - self.assertEqual(field.check(from_model=Event), [ - Error( - "'Invitation.invitee' is not a foreign key to 'Event'.", - hint="Did you mean one of the following foreign keys to 'Event': event?", - obj=field, - id='fields.E339', - ), - Error( - "'Invitation.event' is not a foreign key to 'Fan'.", - hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", - obj=field, - id='fields.E339', - ), - ]) + inviter = models.ForeignKey(Fan, models.CASCADE, related_name="+") + + field = Event._meta.get_field("invitees") + self.assertEqual( + field.check(from_model=Event), + [ + Error( + "'Invitation.invitee' is not a foreign key to 'Event'.", + hint="Did you mean one of the following foreign keys to 'Event': event?", + obj=field, + id="fields.E339", + ), + Error( + "'Invitation.event' is not a foreign key to 'Fan'.", + hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", + obj=field, + id="fields.E339", + ), + ], + ) def test_invalid_field(self): """ Providing invalid field names to ManyToManyField.through_fields triggers validation errors. """ + class Fan(models.Model): pass class Event(models.Model): invitees = models.ManyToManyField( Fan, - through='Invitation', - through_fields=('invalid_field_1', 'invalid_field_2'), + through="Invitation", + through_fields=("invalid_field_1", "invalid_field_2"), ) class Invitation(models.Model): event = models.ForeignKey(Event, models.CASCADE) invitee = models.ForeignKey(Fan, models.CASCADE) - inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+') - - field = Event._meta.get_field('invitees') - self.assertEqual(field.check(from_model=Event), [ - Error( - "The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_1'.", - hint="Did you mean one of the following foreign keys to 'Event': event?", - obj=field, - id='fields.E338', - ), - Error( - "The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_2'.", - hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", - obj=field, - id='fields.E338', - ), - ]) + inviter = models.ForeignKey(Fan, models.CASCADE, related_name="+") + + field = Event._meta.get_field("invitees") + self.assertEqual( + field.check(from_model=Event), + [ + Error( + "The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_1'.", + hint="Did you mean one of the following foreign keys to 'Event': event?", + obj=field, + id="fields.E338", + ), + Error( + "The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_2'.", + hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", + obj=field, + id="fields.E338", + ), + ], + ) def test_explicit_field_names(self): """ If ``through_fields`` kwarg is given, it must specify both link fields of the intermediary table. """ + class Fan(models.Model): pass class Event(models.Model): - invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=(None, 'invitee')) + invitees = models.ManyToManyField( + Fan, through="Invitation", through_fields=(None, "invitee") + ) class Invitation(models.Model): event = models.ForeignKey(Event, models.CASCADE) invitee = models.ForeignKey(Fan, models.CASCADE) - inviter = models.ForeignKey(Fan, models.CASCADE, related_name='+') - - field = Event._meta.get_field('invitees') - self.assertEqual(field.check(from_model=Event), [ - Error( - "Field specifies 'through_fields' but does not provide the names " - "of the two link fields that should be used for the relation " - "through model 'invalid_models_tests.Invitation'.", - hint="Make sure you specify 'through_fields' as through_fields=('field1', 'field2')", - obj=field, - id='fields.E337', - ), - ]) + inviter = models.ForeignKey(Fan, models.CASCADE, related_name="+") + + field = Event._meta.get_field("invitees") + self.assertEqual( + field.check(from_model=Event), + [ + Error( + "Field specifies 'through_fields' but does not provide the names " + "of the two link fields that should be used for the relation " + "through model 'invalid_models_tests.Invitation'.", + hint="Make sure you specify 'through_fields' as through_fields=('field1', 'field2')", + obj=field, + id="fields.E337", + ), + ], + ) def test_superset_foreign_object(self): class Parent(models.Model): @@ -1707,7 +1925,7 @@ class M2mThroughFieldsTests(SimpleTestCase): c = models.PositiveIntegerField() class Meta: - unique_together = (('a', 'b', 'c'),) + unique_together = (("a", "b", "c"),) class Child(models.Model): a = models.PositiveIntegerField() @@ -1716,25 +1934,28 @@ class M2mThroughFieldsTests(SimpleTestCase): parent = models.ForeignObject( Parent, on_delete=models.SET_NULL, - from_fields=('a', 'b'), - to_fields=('a', 'b'), - related_name='children', + from_fields=("a", "b"), + to_fields=("a", "b"), + related_name="children", ) - field = Child._meta.get_field('parent') - self.assertEqual(field.check(from_model=Child), [ - Error( - "No subset of the fields 'a', 'b' on model 'Parent' is unique.", - hint=( - 'Mark a single field as unique=True or add a set of ' - 'fields to a unique constraint (via unique_together or a ' - 'UniqueConstraint (without condition) in the model ' - 'Meta.constraints).' + field = Child._meta.get_field("parent") + self.assertEqual( + field.check(from_model=Child), + [ + Error( + "No subset of the fields 'a', 'b' on model 'Parent' is unique.", + hint=( + "Mark a single field as unique=True or add a set of " + "fields to a unique constraint (via unique_together or a " + "UniqueConstraint (without condition) in the model " + "Meta.constraints)." + ), + obj=field, + id="fields.E310", ), - obj=field, - id='fields.E310', - ), - ]) + ], + ) def test_intersection_foreign_object(self): class Parent(models.Model): @@ -1744,7 +1965,7 @@ class M2mThroughFieldsTests(SimpleTestCase): d = models.PositiveIntegerField() class Meta: - unique_together = (('a', 'b', 'c'),) + unique_together = (("a", "b", "c"),) class Child(models.Model): a = models.PositiveIntegerField() @@ -1754,22 +1975,25 @@ class M2mThroughFieldsTests(SimpleTestCase): parent = models.ForeignObject( Parent, on_delete=models.SET_NULL, - from_fields=('a', 'b', 'd'), - to_fields=('a', 'b', 'd'), - related_name='children', + from_fields=("a", "b", "d"), + to_fields=("a", "b", "d"), + related_name="children", ) - field = Child._meta.get_field('parent') - self.assertEqual(field.check(from_model=Child), [ - Error( - "No subset of the fields 'a', 'b', 'd' on model 'Parent' is unique.", - hint=( - 'Mark a single field as unique=True or add a set of ' - 'fields to a unique constraint (via unique_together or a ' - 'UniqueConstraint (without condition) in the model ' - 'Meta.constraints).' + field = Child._meta.get_field("parent") + self.assertEqual( + field.check(from_model=Child), + [ + Error( + "No subset of the fields 'a', 'b', 'd' on model 'Parent' is unique.", + hint=( + "Mark a single field as unique=True or add a set of " + "fields to a unique constraint (via unique_together or a " + "UniqueConstraint (without condition) in the model " + "Meta.constraints)." + ), + obj=field, + id="fields.E310", ), - obj=field, - id='fields.E310', - ), - ]) + ], + ) |