diff --git a/django/contrib/contenttypes/apps.py b/django/contrib/contenttypes/apps.py index 095dbf56150..1a8e25b98e2 100644 --- a/django/contrib/contenttypes/apps.py +++ b/django/contrib/contenttypes/apps.py @@ -1,5 +1,7 @@ from django.apps import AppConfig -from django.contrib.contenttypes.checks import check_generic_foreign_keys +from django.contrib.contenttypes.checks import ( + check_generic_foreign_keys, 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 _ @@ -17,3 +19,4 @@ class ContentTypesConfig(AppConfig): 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 d21df40f46d..3e802ea26b6 100644 --- a/django/contrib/contenttypes/checks.py +++ b/django/contrib/contenttypes/checks.py @@ -1,6 +1,7 @@ from itertools import chain from django.apps import apps +from django.core.checks import Error def check_generic_foreign_keys(app_configs=None, **kwargs): @@ -18,3 +19,23 @@ def check_generic_foreign_keys(app_configs=None, **kwargs): 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() + else: + models = chain.from_iterable(app_config.get_models() for app_config in app_configs) + errors = [] + for model in models: + if len(model._meta.model_name) > 100: + errors.append( + Error( + 'Model names must be at most 100 characters (got %d).' % ( + len(model._meta.model_name), + ), + obj=model, + id='contenttypes.E005', + ) + ) + return errors diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 64fd6b68aac..b4d1fd40894 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -668,6 +668,7 @@ The following checks are performed when a model contains a * **contenttypes.E003**: ```` is not a ``ForeignKey``. * **contenttypes.E004**: ```` is not a ``ForeignKey`` to ``contenttypes.ContentType``. +* **contenttypes.E005**: Model names must be at most 100 characters. ``sites`` --------- diff --git a/tests/contenttypes_tests/test_checks.py b/tests/contenttypes_tests/test_checks.py index 6b33107de2e..44cd3c27585 100644 --- a/tests/contenttypes_tests/test_checks.py +++ b/tests/contenttypes_tests/test_checks.py @@ -1,5 +1,6 @@ from unittest import mock +from django.contrib.contenttypes.checks import check_model_name_lengths from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) @@ -216,3 +217,20 @@ class GenericRelationTests(SimpleTestCase): id='fields.E001', ) ]) + + +@isolate_apps('contenttypes_tests', attr_name='apps') +class ModelCheckTests(SimpleTestCase): + def test_model_name_too_long(self): + model = type('A' * 101, (models.Model,), {'__module__': self.__module__}) + self.assertEqual(check_model_name_lengths(self.apps.get_app_configs()), [ + checks.Error( + 'Model names must be at most 100 characters (got 101).', + obj=model, + id='contenttypes.E005', + ) + ]) + + def test_model_name_max_length(self): + type('A' * 100, (models.Model,), {'__module__': self.__module__}) + self.assertEqual(check_model_name_lengths(self.apps.get_app_configs()), [])