Browse Source

Deprecated importing a model before loading its application.

Refs #21719, #21680.
Aymeric Augustin 11 years ago
parent
commit
3326a412cc

+ 9 - 1
django/db/models/base.py

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 import copy
 import sys
 from functools import update_wrapper
-from django.utils.six.moves import zip
+import warnings
 
 from django.apps import apps
 from django.apps.base import MODELS_MODULE_NAME
@@ -25,6 +25,7 @@ from django.utils.translation import ugettext_lazy as _
 from django.utils.functional import curry
 from django.utils.encoding import force_str, force_text
 from django.utils import six
+from django.utils.six.moves import zip
 from django.utils.text import get_text_list, capfirst
 
 
@@ -103,6 +104,13 @@ class ModelBase(type):
                 # For 'django.contrib.sites.models', this would be 'sites'.
                 # For 'geo.models.places' this would be 'geo'.
 
+                warnings.warn(
+                    "Model class %s.%s doesn't declare an explicit app_label "
+                    "and either isn't in an application in  INSTALLED_APPS "
+                    "or else was imported before its application was loaded. "
+                    "This will no longer be supported in Django 1.9."
+                    % (module, name), PendingDeprecationWarning, stacklevel=2)
+
                 model_module = sys.modules[new_class.__module__]
                 package_components = model_module.__name__.split('.')
                 package_components.reverse()  # find the last occurrence of 'models'

+ 6 - 0
docs/internals/deprecation.txt

@@ -170,6 +170,12 @@ these changes.
 * ``allow_syncdb`` on database routers will no longer automatically become
   ``allow_migrate``.
 
+* All models will need to be defined inside an installed application or
+  declare an explicit :attr:`~django.db.models.Options.app_label`.
+  Furthermore, it won't be possible to import them before their application
+  is loaded. In particular, it won't be possible to import models inside
+  the root package of their application.
+
 * If models are organized in a package, Django will no longer look for
   :ref:`initial SQL data<initial-sql>` in ``myapp/models/sql/``. Move your
   custom SQL files to ``myapp/sql/``.

+ 7 - 0
docs/ref/applications.txt

@@ -181,6 +181,13 @@ Methods
     as registering signals. It is called as soon as the registry is fully
     populated.
 
+    You cannot import models in modules that define application configuration
+    classes, but you can use :meth:`get_model` to access a model class by
+    name, like this::
+
+        def ready(self):
+            MyModel = self.get_model('MyModel')
+
 Application registry
 ====================
 

+ 12 - 3
docs/releases/1.7.txt

@@ -677,9 +677,18 @@ directory and a subdirectory on :envvar:`PYTHONPATH`. Refer to the section on
 the new project layout in the :doc:`1.4 release notes </releases/1.4>` for
 migration instructions.
 
-You should make sure that your project doesn't import models from applications
-that aren't in :setting:`INSTALLED_APPS`. Relations involving such models may
-not be created properly. Future versions of Django may forbid this entirely.
+You should make sure that:
+
+* All models are defined in applications that are listed in
+  :setting:`INSTALLED_APPS` or have an explicit
+  :attr:`~django.db.models.Options.app_label`.
+
+* Models aren't imported as a side-effect of loading their application.
+  Specifically, you shouldn't import models in the root module of an
+  application nor in the module that define its configuration class.
+
+Django will enforce these requirements as of version 1.9, after a deprecation
+period.
 
 Subclassing AppCommand
 ^^^^^^^^^^^^^^^^^^^^^^

+ 6 - 1
tests/runtests.py

@@ -130,6 +130,11 @@ def setup(verbosity, test_labels):
         'django.contrib.comments is deprecated and will be removed before Django 1.8.',
         DeprecationWarning
     )
+    warnings.filterwarnings(
+        'ignore',
+        'Model class django.contrib.comments.models.*supported in Django 1.9.',
+        PendingDeprecationWarning
+    )
     # Load all the ALWAYS_INSTALLED_APPS.
     django.setup()
 
@@ -169,8 +174,8 @@ def setup(verbosity, test_labels):
             if module_label not in settings.INSTALLED_APPS:
                 settings.INSTALLED_APPS.append(module_label)
             app_config = AppConfig.create(module_label)
-            app_config.import_models(apps.all_models[app_config.label])
             apps.app_configs[app_config.label] = app_config
+            app_config.import_models(apps.all_models[app_config.label])
             apps.clear_cache()
 
     return state