diff --git a/django/contrib/contenttypes/apps.py b/django/contrib/contenttypes/apps.py index 11dfb91010..af03301587 100644 --- a/django/contrib/contenttypes/apps.py +++ b/django/contrib/contenttypes/apps.py @@ -1,8 +1,5 @@ from django.apps import AppConfig -from django.contrib.contenttypes.checks import ( - check_generic_foreign_keys, - check_model_name_lengths, -) +from django.contrib.contenttypes.checks import check_model_name_lengths from django.core import checks from django.db.models.signals import post_migrate, pre_migrate from django.utils.translation import gettext_lazy as _ @@ -18,5 +15,4 @@ class ContentTypesConfig(AppConfig): def ready(self): pre_migrate.connect(inject_rename_contenttypes_operations, sender=self) post_migrate.connect(create_contenttypes) - checks.register(check_generic_foreign_keys, checks.Tags.models) checks.register(check_model_name_lengths, checks.Tags.models) diff --git a/django/contrib/contenttypes/checks.py b/django/contrib/contenttypes/checks.py index 753c5d22f8..a567a3ace8 100644 --- a/django/contrib/contenttypes/checks.py +++ b/django/contrib/contenttypes/checks.py @@ -4,27 +4,6 @@ from django.apps import apps from django.core.checks import Error -def check_generic_foreign_keys(app_configs=None, **kwargs): - from .fields import GenericForeignKey - - if app_configs is None: - models = apps.get_models() - else: - models = chain.from_iterable( - app_config.get_models() for app_config in app_configs - ) - errors = [] - fields = ( - obj - for model in models - for obj in vars(model).values() - if isinstance(obj, GenericForeignKey) - ) - for field in fields: - errors.extend(field.check()) - return errors - - def check_model_name_lengths(app_configs=None, **kwargs): if app_configs is None: models = apps.get_models() diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 770f88265c..4c7390e36e 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -357,7 +357,10 @@ class GenericRelation(ForeignObject): def check(self, **kwargs): return [ - *super().check(**kwargs), + *self._check_field_name(), + *self._check_related_query_name_is_valid(), + *self._check_relation_model_exists(), + *self._check_referencing_to_swapped_model(), *self._check_generic_foreign_key_existence(), ] diff --git a/django/db/models/base.py b/django/db/models/base.py index 75328e0749..c81bcf2f24 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1789,6 +1789,8 @@ class Model(AltersData, metaclass=ModelBase): errors = [] for field in cls._meta.local_fields: errors.extend(field.check(**kwargs)) + for field in cls._meta.private_fields: + errors.extend(field.check(**kwargs)) for field in cls._meta.local_many_to_many: errors.extend(field.check(from_model=cls, **kwargs)) return errors diff --git a/tests/contenttypes_tests/test_checks.py b/tests/contenttypes_tests/test_checks.py index 50730c5a1d..42b27bcf97 100644 --- a/tests/contenttypes_tests/test_checks.py +++ b/tests/contenttypes_tests/test_checks.py @@ -122,10 +122,10 @@ class GenericForeignKeyTests(SimpleTestCase): with mock.patch.object(GenericForeignKey, "check") as check: checks.run_checks(app_configs=self.apps.get_app_configs()) - check.assert_called_once_with() + check.assert_called_once_with(databases=None) -@isolate_apps("contenttypes_tests") +@isolate_apps("contenttypes_tests", attr_name="apps") class GenericRelationTests(SimpleTestCase): def test_valid_generic_relationship(self): class TaggedItem(models.Model): @@ -253,6 +253,19 @@ class GenericRelationTests(SimpleTestCase): ], ) + def test_generic_relation_checks_are_performed(self): + class TaggedItem(models.Model): + content_type = models.ForeignKey(ContentType, models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey() + + class InvalidBookmark(models.Model): + tags_ = GenericRelation("TaggedItem") + + with mock.patch.object(GenericRelation, "check") as check: + checks.run_checks(app_configs=self.apps.get_app_configs()) + check.assert_called_once_with(databases=None) + @isolate_apps("contenttypes_tests", attr_name="apps") class ModelCheckTests(SimpleTestCase): diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index e34f3c8947..4a0c78d35b 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -411,6 +411,12 @@ class RelatedJSONModel(models.Model): required_db_features = {"supports_json_field"} +class GfkModel(models.Model): + object_id = models.PositiveIntegerField() + content_type = models.ForeignKey(ContentType, models.CASCADE) + gfk = GenericForeignKey() + + class AllFieldsModel(models.Model): big_integer = models.BigIntegerField() binary = models.BinaryField() @@ -448,7 +454,7 @@ class AllFieldsModel(models.Model): object_id = models.PositiveIntegerField() content_type = models.ForeignKey(ContentType, models.CASCADE) gfk = GenericForeignKey() - gr = GenericRelation(DataModel) + gr = GenericRelation(GfkModel) class ManyToMany(models.Model): diff --git a/tests/model_meta/models.py b/tests/model_meta/models.py index bc69d61a59..44ec3e4981 100644 --- a/tests/model_meta/models.py +++ b/tests/model_meta/models.py @@ -5,7 +5,9 @@ from django.utils.translation import gettext_lazy as _ class Relation(models.Model): - pass + content_type = models.ForeignKey(ContentType, models.CASCADE, related_name="+") + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey() class InstanceOnlyDescriptor: