diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 5777ebc3b1..4c2247c08b 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -214,14 +214,16 @@ class RelatedField(FieldCacheMixin, Field): rel_is_hidden = self.remote_field.is_hidden() rel_name = self.remote_field.get_accessor_name() # i. e. "model_set" rel_query_name = self.related_query_name() # i. e. "model" - field_name = "%s.%s" % (opts.object_name, self.name) # i. e. "Model.field" + # i.e. "app_label.Model.field". + field_name = '%s.%s' % (opts.label, self.name) # Check clashes between accessor or reverse query name of `field` # and any other field name -- i.e. accessor for Model.foreign is # model_set and it clashes with Target.model_set. potential_clashes = rel_opts.fields + rel_opts.many_to_many for clash_field in potential_clashes: - clash_name = "%s.%s" % (rel_opts.object_name, clash_field.name) # i.e. "Target.model_set" + # i.e. "app_label.Target.model_set". + clash_name = '%s.%s' % (rel_opts.label, clash_field.name) if not rel_is_hidden and clash_field.name == rel_name: errors.append( checks.Error( @@ -249,9 +251,11 @@ class RelatedField(FieldCacheMixin, Field): # Model.m2m accessor. potential_clashes = (r for r in rel_opts.related_objects if r.field is not self) for clash_field in potential_clashes: - clash_name = "%s.%s" % ( # i. e. "Model.m2m" - clash_field.related_model._meta.object_name, - clash_field.field.name) + # i.e. "app_label.Model.m2m". + clash_name = '%s.%s' % ( + clash_field.related_model._meta.label, + clash_field.field.name, + ) if not rel_is_hidden and clash_field.get_accessor_name() == rel_name: errors.append( checks.Error( diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 1066417f2e..6b66b18112 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -239,14 +239,15 @@ Related fields either not installed, or is abstract. * **fields.E301**: Field defines a relation with the model ``.`` which has been swapped out. -* **fields.E302**: Accessor for field ``.`` clashes with - field ``.``. -* **fields.E303**: Reverse query name for field ``.`` - clashes with field ``.``. -* **fields.E304**: Field name ``.`` clashes with accessor - for ``.``. -* **fields.E305**: Field name ``.`` clashes with reverse - query name for ``.``. +* **fields.E302**: Accessor for field ``..`` + clashes with field ``..``. +* **fields.E303**: Reverse query name for field + ``..`` clashes with field + ``..``. +* **fields.E304**: Field name ``..`` clashes with + accessor for ``..``. +* **fields.E305**: Field name ``..`` clashes with + reverse query name for ``..``. * **fields.E306**: Related name must be a valid Python identifier or end with a ``'+'``. * **fields.E307**: The field ``..`` was declared diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py index 7e32cced60..82b1b426cf 100644 --- a/tests/invalid_models_tests/test_relative_fields.py +++ b/tests/invalid_models_tests/test_relative_fields.py @@ -874,10 +874,14 @@ class AccessorClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.rel' clashes with field name 'Target.model_set'.", - hint=("Rename field 'Target.model_set', or add/change " - "a related_name argument to the definition " - "for field 'Model.rel'."), + "Reverse accessor 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', ), @@ -893,19 +897,25 @@ class AccessorClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.foreign' clashes with reverse accessor for 'Model.m2m'.", + "Reverse accessor 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 'Model.foreign' or 'Model.m2m'." + "for 'invalid_models_tests.Model.foreign' or " + "'invalid_models_tests.Model.m2m'." ), obj=Model._meta.get_field('foreign'), id='fields.E304', ), Error( - "Reverse accessor for 'Model.m2m' clashes with reverse accessor for 'Model.foreign'.", + "Reverse accessor 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 'Model.m2m' or 'Model.foreign'." + "for 'invalid_models_tests.Model.m2m' or " + "'invalid_models_tests.Model.foreign'." ), obj=Model._meta.get_field('m2m'), id='fields.E304', @@ -929,10 +939,13 @@ class AccessorClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.children' clashes with field name 'Child.m2m_clash'.", + "Reverse accessor for 'invalid_models_tests.Model.children' " + "clashes with field name " + "'invalid_models_tests.Child.m2m_clash'.", hint=( - "Rename field 'Child.m2m_clash', or add/change a related_name " - "argument to the definition for field 'Model.children'." + "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', @@ -1000,10 +1013,12 @@ class ReverseQueryNameClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse query name for 'Model.rel' clashes with field name 'Target.model'.", + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.model'.", hint=( - "Rename field 'Target.model', or add/change a related_name " - "argument to the definition for field 'Model.rel'." + "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', @@ -1056,19 +1071,23 @@ class ExplicitRelatedNameClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.rel' clashes with field name 'Target.clash'.", + "Reverse accessor for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.clash'.", hint=( - "Rename field 'Target.clash', or add/change a related_name " - "argument to the definition for field 'Model.rel'." + "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', ), Error( - "Reverse query name for 'Model.rel' clashes with field name 'Target.clash'.", + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.clash'.", hint=( - "Rename field 'Target.clash', or add/change a related_name " - "argument to the definition for field 'Model.rel'." + "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', @@ -1162,10 +1181,12 @@ class ExplicitRelatedQueryNameClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse query name for 'Model.rel' clashes with field name 'Target.clash'.", + "Reverse query name for 'invalid_models_tests.Model.rel' " + "clashes with field name 'invalid_models_tests.Target.clash'.", hint=( - "Rename field 'Target.clash', or add/change a related_name " - "argument to the definition for field 'Model.rel'." + "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', @@ -1183,19 +1204,25 @@ class SelfReferentialM2MClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.first_m2m' clashes with reverse accessor for 'Model.second_m2m'.", + "Reverse accessor 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 'Model.first_m2m' or 'Model.second_m2m'." + "for 'invalid_models_tests.Model.first_m2m' or " + "'invalid_models_tests.Model.second_m2m'." ), obj=Model._meta.get_field('first_m2m'), id='fields.E304', ), Error( - "Reverse accessor for 'Model.second_m2m' clashes with reverse accessor for 'Model.first_m2m'.", + "Reverse accessor 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 'Model.second_m2m' or 'Model.first_m2m'." + "for 'invalid_models_tests.Model.second_m2m' or " + "'invalid_models_tests.Model.first_m2m'." ), obj=Model._meta.get_field('second_m2m'), id='fields.E304', @@ -1208,10 +1235,13 @@ class SelfReferentialM2MClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.model_set' clashes with field name 'Model.model_set'.", + "Reverse accessor for 'invalid_models_tests.Model.model_set' " + "clashes with field name " + "'invalid_models_tests.Model.model_set'.", hint=( - "Rename field 'Model.model_set', or add/change a related_name " - "argument to the definition for field 'Model.model_set'." + "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', @@ -1224,10 +1254,12 @@ class SelfReferentialM2MClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse query name for 'Model.model' clashes with field name 'Model.model'.", + "Reverse query name for 'invalid_models_tests.Model.model' " + "clashes with field name 'invalid_models_tests.Model.model'.", hint=( - "Rename field 'Model.model', or add/change a related_name " - "argument to the definition for field 'Model.model'." + "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', @@ -1241,19 +1273,23 @@ class SelfReferentialM2MClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.m2m' clashes with field name 'Model.clash'.", + "Reverse accessor for 'invalid_models_tests.Model.m2m' " + "clashes with field name 'invalid_models_tests.Model.clash'.", hint=( - "Rename field 'Model.clash', or add/change a related_name " - "argument to the definition for field 'Model.m2m'." + "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', ), Error( - "Reverse query name for 'Model.m2m' clashes with field name 'Model.clash'.", + "Reverse query name for 'invalid_models_tests.Model.m2m' " + "clashes with field name 'invalid_models_tests.Model.clash'.", hint=( - "Rename field 'Model.clash', or add/change a related_name " - "argument to the definition for field 'Model.m2m'." + "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', @@ -1277,11 +1313,13 @@ class SelfReferentialFKClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.model_set' clashes with field name 'Model.model_set'.", + "Reverse accessor for 'invalid_models_tests.Model.model_set' " + "clashes with field name " + "'invalid_models_tests.Model.model_set'.", hint=( - "Rename field 'Model.model_set', or add/change " - "a related_name argument to the definition " - "for field 'Model.model_set'." + "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', @@ -1294,10 +1332,12 @@ class SelfReferentialFKClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse query name for 'Model.model' clashes with field name 'Model.model'.", + "Reverse query name for 'invalid_models_tests.Model.model' " + "clashes with field name 'invalid_models_tests.Model.model'.", hint=( - "Rename field 'Model.model', or add/change a related_name " - "argument to the definition for field 'Model.model'." + "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', @@ -1311,19 +1351,23 @@ class SelfReferentialFKClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.foreign' clashes with field name 'Model.clash'.", + "Reverse accessor for 'invalid_models_tests.Model.foreign' " + "clashes with field name 'invalid_models_tests.Model.clash'.", hint=( - "Rename field 'Model.clash', or add/change a related_name " - "argument to the definition for field 'Model.foreign'." + "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', ), Error( - "Reverse query name for 'Model.foreign' clashes with field name 'Model.clash'.", + "Reverse query name for 'invalid_models_tests.Model.foreign' " + "clashes with field name 'invalid_models_tests.Model.clash'.", hint=( - "Rename field 'Model.clash', or add/change a related_name " - "argument to the definition for field 'Model.foreign'." + "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', @@ -1355,89 +1399,144 @@ class ComplexClashTests(SimpleTestCase): self.assertEqual(Model.check(), [ Error( - "Reverse accessor for 'Model.foreign_1' clashes with field name 'Target.id'.", - hint=("Rename field 'Target.id', or add/change a related_name " - "argument to the definition for field 'Model.foreign_1'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.foreign_1' clashes with field name 'Target.id'.", - hint=("Rename field 'Target.id', or add/change a related_name " - "argument to the definition for field 'Model.foreign_1'."), + "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', ), Error( - "Reverse accessor for 'Model.foreign_1' clashes with reverse accessor for 'Model.m2m_1'.", - hint=("Add or change a related_name argument to " - "the definition for 'Model.foreign_1' or 'Model.m2m_1'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.foreign_1' clashes with reverse query name for 'Model.m2m_1'.", - hint=("Add or change a related_name argument to " - "the definition for 'Model.foreign_1' or 'Model.m2m_1'."), + "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', ), Error( - "Reverse accessor for 'Model.foreign_2' clashes with reverse accessor for 'Model.m2m_2'.", - hint=("Add or change a related_name argument " - "to the definition for 'Model.foreign_2' or 'Model.m2m_2'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.foreign_2' clashes with reverse query name for 'Model.m2m_2'.", - hint=("Add or change a related_name argument to " - "the definition for 'Model.foreign_2' or 'Model.m2m_2'."), + "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', ), Error( - "Reverse accessor for 'Model.m2m_1' clashes with field name 'Target.id'.", - hint=("Rename field 'Target.id', or add/change a related_name " - "argument to the definition for field 'Model.m2m_1'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.m2m_1' clashes with field name 'Target.id'.", - hint=("Rename field 'Target.id', or add/change a related_name " - "argument to the definition for field 'Model.m2m_1'."), + "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', ), Error( - "Reverse accessor for 'Model.m2m_1' clashes with reverse accessor for 'Model.foreign_1'.", - hint=("Add or change a related_name argument to the definition " - "for 'Model.m2m_1' or 'Model.foreign_1'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.m2m_1' clashes with reverse query name for 'Model.foreign_1'.", - hint=("Add or change a related_name argument to " - "the definition for 'Model.m2m_1' or 'Model.foreign_1'."), + "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', ), - Error( - "Reverse accessor for 'Model.m2m_2' clashes with reverse accessor for 'Model.foreign_2'.", - hint=("Add or change a related_name argument to the definition " - "for 'Model.m2m_2' or 'Model.foreign_2'."), + "Reverse accessor 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', ), Error( - "Reverse query name for 'Model.m2m_2' clashes with reverse query name for 'Model.foreign_2'.", - hint=("Add or change a related_name argument to the definition " - "for 'Model.m2m_2' or 'Model.foreign_2'."), + "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', ), @@ -1458,11 +1557,13 @@ class ComplexClashTests(SimpleTestCase): ] self.assertEqual(Child.check(), [ Error( - "Reverse %s for 'Child.%s' clashes with reverse %s for " - "'Child.%s'." % (attr, field_name, attr, clash_name), + "Reverse %s for 'invalid_models_tests.Child.%s' clashes with " + "reverse %s for 'invalid_models_tests.Child.%s'." + % (attr, field_name, attr, clash_name), hint=( "Add or change a related_name argument to the definition " - "for 'Child.%s' or 'Child.%s'." % (field_name, clash_name) + "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, diff --git a/tests/model_inheritance/test_abstract_inheritance.py b/tests/model_inheritance/test_abstract_inheritance.py index 1293efc531..8caa5153fa 100644 --- a/tests/model_inheritance/test_abstract_inheritance.py +++ b/tests/model_inheritance/test_abstract_inheritance.py @@ -225,19 +225,24 @@ class AbstractInheritanceTests(SimpleTestCase): Foo._meta.get_field('foo').check(), [ Error( - "Reverse accessor for 'Foo.foo' clashes with field name 'Descendant.foo'.", + "Reverse accessor for 'model_inheritance.Foo.foo' clashes " + "with field name 'model_inheritance.Descendant.foo'.", hint=( - "Rename field 'Descendant.foo', or add/change a related_name " - "argument to the definition for field 'Foo.foo'." + "Rename field 'model_inheritance.Descendant.foo', or " + "add/change a related_name argument to the definition " + "for field 'model_inheritance.Foo.foo'." ), obj=Foo._meta.get_field('foo'), id='fields.E302', ), Error( - "Reverse query name for 'Foo.foo' clashes with field name 'Descendant.foo'.", + "Reverse query name for 'model_inheritance.Foo.foo' " + "clashes with field name " + "'model_inheritance.Descendant.foo'.", hint=( - "Rename field 'Descendant.foo', or add/change a related_name " - "argument to the definition for field 'Foo.foo'." + "Rename field 'model_inheritance.Descendant.foo', or " + "add/change a related_name argument to the definition " + "for field 'model_inheritance.Foo.foo'." ), obj=Foo._meta.get_field('foo'), id='fields.E303',