diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 6a9cb12a90..dd4c09a4e3 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1707,13 +1707,18 @@ class ManyToManyField(RelatedField): and getattr(field.remote_field, "model", None) == related_model ): + related_object_name = ( + related_model + if isinstance(related_model, str) + else related_model._meta.object_name + ) errors.append( checks.Error( "'%s.%s' is not a foreign key to '%s'." % ( through._meta.object_name, field_name, - related_model._meta.object_name, + related_object_name, ), hint=hint, obj=self, diff --git a/tests/invalid_models_tests/test_relative_fields.py b/tests/invalid_models_tests/test_relative_fields.py index 4167e0712a..5a791d1fcc 100644 --- a/tests/invalid_models_tests/test_relative_fields.py +++ b/tests/invalid_models_tests/test_relative_fields.py @@ -2185,3 +2185,45 @@ class M2mThroughFieldsTests(SimpleTestCase): ), ], ) + + def test_invalid_to_argument_with_through(self): + class Foo(models.Model): + pass + + class Bar(models.Model): + foos = models.ManyToManyField( + to="Fo", + through="FooBar", + through_fields=("bar", "foo"), + ) + + class FooBar(models.Model): + foo = models.ForeignKey("Foo", on_delete=models.CASCADE) + bar = models.ForeignKey("Bar", on_delete=models.CASCADE) + + field = Bar._meta.get_field("foos") + + self.assertEqual( + field.check(from_model=Bar), + [ + Error( + "Field defines a relation with model 'Fo', " + "which is either not installed, or is abstract.", + obj=field, + id="fields.E300", + ), + Error( + "The model is used as an intermediate model by " + "'invalid_models_tests.Bar.foos', " + "but it does not have a foreign key to 'Bar' " + "or 'invalid_models_tests.Fo'.", + obj=FooBar, + id="fields.E336", + ), + Error( + "'FooBar.foo' is not a foreign key to 'Fo'.", + obj=field, + id="fields.E339", + ), + ], + )