diff --git a/django/db/models/base.py b/django/db/models/base.py index 531dd8adcc4..d7b54b9268f 100644 --- a/django/db/models/base.py +++ b/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' diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 1b8b45a5207..514561e1ff8 100644 --- a/docs/internals/deprecation.txt +++ b/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` in ``myapp/models/sql/``. Move your custom SQL files to ``myapp/sql/``. diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt index a44062808e0..e496f244878 100644 --- a/docs/ref/applications.txt +++ b/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 ==================== diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index a8becf1babd..9fcad389319 100644 --- a/docs/releases/1.7.txt +++ b/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 ` 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 ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/runtests.py b/tests/runtests.py index bb14a27fd65..dcd9c885bda 100755 --- a/tests/runtests.py +++ b/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