Deprecated importing a model before loading its application.

Refs #21719, #21680.
This commit is contained in:
Aymeric Augustin 2014-01-10 23:06:19 +01:00
parent 81bb8d1220
commit 3326a412cc
5 changed files with 40 additions and 5 deletions

View File

@ -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'

View File

@ -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/``.

View File

@ -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
====================

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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