diff --git a/django/db/models/base.py b/django/db/models/base.py index b223764879..11dcb5ba3e 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -194,9 +194,6 @@ class ModelBase(type): base = parent if base is None: raise TypeError("Proxy model '%s' has no non-abstract model base class." % name) - if (new_class._meta.local_fields or - new_class._meta.local_many_to_many): - raise FieldError("Proxy model '%s' contains model fields." % name) new_class._meta.setup_proxy(base) new_class._meta.concrete_model = base._meta.concrete_model else: @@ -1047,6 +1044,7 @@ class Model(six.with_metaclass(ModelBase)): def check(cls, **kwargs): errors = [] errors.extend(cls._check_swappable()) + errors.extend(cls._check_model()) errors.extend(cls._check_managers(**kwargs)) if not cls._meta.swapped: errors.extend(cls._check_fields(**kwargs)) @@ -1094,6 +1092,21 @@ class Model(six.with_metaclass(ModelBase)): ) return errors + @classmethod + def _check_model(cls): + errors = [] + if cls._meta.proxy: + if cls._meta.local_fields or cls._meta.local_many_to_many: + errors.append( + checks.Error( + "Proxy model '%s' contains model fields." % cls.__name__, + hint=None, + obj=None, + id='models.E017', + ) + ) + return errors + @classmethod def _check_managers(cls, **kwargs): """ Perform all manager checks. """ diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 8cdeb1df91..65f3d95453 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -45,6 +45,7 @@ Models * **models.E014**: ``ordering`` must be a tuple or list (even if you want to order by only one field). * **models.E015**: ``ordering`` refers to the non-existent field ````. * **models.E016**: ``index_together/unique_together`` refers to field ```` which is not local to model ````. +* **models.E017**: Proxy model ```` contains model fields. Fields ~~~~~~ diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 0052ff4630..7cb5bdf441 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -4,7 +4,7 @@ from django.apps import apps from django.contrib import admin from django.contrib.contenttypes.models import ContentType from django.core import management -from django.core.exceptions import FieldError +from django.core import checks from django.db import models, DEFAULT_DB_ALIAS from django.db.models import signals from django.test import TestCase, override_settings @@ -143,13 +143,25 @@ class ProxyModelTests(TestCase): self.assertRaises(TypeError, build_no_base_classes) def test_new_fields(self): - def build_new_fields(): - class NoNewFields(Person): - newfield = models.BooleanField() + class NoNewFields(Person): + newfield = models.BooleanField() - class Meta: - proxy = True - self.assertRaises(FieldError, build_new_fields) + class Meta: + proxy = True + # don't register this model in the app_cache for the current app, + # otherwise the check fails when other tests are being run. + app_label = 'no_such_app' + + errors = NoNewFields.check() + expected = [ + checks.Error( + "Proxy model 'NoNewFields' contains model fields.", + hint=None, + obj=None, + id='models.E017', + ) + ] + self.assertEqual(errors, expected) @override_settings(TEST_SWAPPABLE_MODEL='proxy_models.AlternateModel') def test_swappable(self):