Browse Source

Fixed #22690 -- Added a check for proxy models containing fields.

Removed the FieldError raised by ModelBase.__new__ in this case.
Craig de Stigter 10 years ago
parent
commit
ce993efda8
3 changed files with 37 additions and 11 deletions
  1. 16 3
      django/db/models/base.py
  2. 1 0
      docs/ref/checks.txt
  3. 20 8
      tests/proxy_models/tests.py

+ 16 - 3
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. """

+ 1 - 0
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 ``<field name>``.
 * **models.E016**: ``index_together/unique_together`` refers to field ``<field_name>`` which is not local to model ``<model>``.
+* **models.E017**: Proxy model ``<model>`` contains model fields.
 
 Fields
 ~~~~~~

+ 20 - 8
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 Meta:
-                    proxy = True
-        self.assertRaises(FieldError, build_new_fields)
+        class NoNewFields(Person):
+            newfield = models.BooleanField()
+
+            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):