Disallowed importing concrete models without an application.
Removed fragile algorithm to find which application a model belongs to. Fixed #21680, #21719. Refs #21794.
This commit is contained in:
parent
c7a6996df7
commit
1b8af4cfa0
|
@ -2,12 +2,10 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
|
||||||
import warnings
|
import warnings
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.apps.config import MODELS_MODULE_NAME
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import checks
|
from django.core import checks
|
||||||
from django.core.exceptions import (
|
from django.core.exceptions import (
|
||||||
|
@ -88,48 +86,24 @@ class ModelBase(type):
|
||||||
meta = attr_meta
|
meta = attr_meta
|
||||||
base_meta = getattr(new_class, '_meta', None)
|
base_meta = getattr(new_class, '_meta', None)
|
||||||
|
|
||||||
|
app_label = None
|
||||||
|
|
||||||
# Look for an application configuration to attach the model to.
|
# Look for an application configuration to attach the model to.
|
||||||
app_config = apps.get_containing_app_config(module)
|
app_config = apps.get_containing_app_config(module)
|
||||||
|
|
||||||
if getattr(meta, 'app_label', None) is None:
|
if getattr(meta, 'app_label', None) is None:
|
||||||
|
|
||||||
if app_config is None:
|
if app_config is None:
|
||||||
# If the model is imported before the configuration for its
|
|
||||||
# application is created (#21719), or isn't in an installed
|
|
||||||
# application (#21680), use the legacy logic to figure out the
|
|
||||||
# app_label by looking one level up from the package or module
|
|
||||||
# named 'models'. If no such package or module exists, fall
|
|
||||||
# back to looking one level up from the module this model is
|
|
||||||
# defined in.
|
|
||||||
|
|
||||||
# For 'django.contrib.sites.models', this would be 'sites'.
|
|
||||||
# For 'geo.models.places' this would be 'geo'.
|
|
||||||
|
|
||||||
msg = (
|
|
||||||
"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))
|
|
||||||
if not abstract:
|
if not abstract:
|
||||||
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
raise RuntimeError(
|
||||||
|
"Model class %s.%s doesn't declare an explicit "
|
||||||
model_module = sys.modules[new_class.__module__]
|
"app_label and either isn't in an application in "
|
||||||
package_components = model_module.__name__.split('.')
|
"INSTALLED_APPS or else was imported before its "
|
||||||
package_components.reverse() # find the last occurrence of 'models'
|
"application was loaded. " % (module, name))
|
||||||
try:
|
|
||||||
app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
|
|
||||||
except ValueError:
|
|
||||||
app_label_index = 1
|
|
||||||
kwargs = {"app_label": package_components[app_label_index]}
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
kwargs = {"app_label": app_config.label}
|
app_label = app_config.label
|
||||||
|
|
||||||
else:
|
new_class.add_to_class('_meta', Options(meta, app_label))
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
new_class.add_to_class('_meta', Options(meta, **kwargs))
|
|
||||||
if not abstract:
|
if not abstract:
|
||||||
new_class.add_to_class(
|
new_class.add_to_class(
|
||||||
'DoesNotExist',
|
'DoesNotExist',
|
||||||
|
|
|
@ -24,9 +24,8 @@ Available ``Meta`` options
|
||||||
|
|
||||||
.. attribute:: Options.app_label
|
.. attribute:: Options.app_label
|
||||||
|
|
||||||
If a model exists outside of an application in :setting:`INSTALLED_APPS` or
|
If a model is defined outside of an application in
|
||||||
if it's imported before its application was loaded, it must define which
|
:setting:`INSTALLED_APPS`, it must declare which app it belongs to::
|
||||||
app it is part of::
|
|
||||||
|
|
||||||
app_label = 'myapp'
|
app_label = 'myapp'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
from django.test.runner import DiscoverRunner
|
||||||
|
|
||||||
|
|
||||||
|
class CustomOptionsTestRunner(DiscoverRunner):
|
||||||
|
|
||||||
|
def __init__(self, verbosity=1, interactive=True, failfast=True, option_a=None, option_b=None, option_c=None, **kwargs):
|
||||||
|
super(CustomOptionsTestRunner, self).__init__(verbosity=verbosity, interactive=interactive,
|
||||||
|
failfast=failfast)
|
||||||
|
self.option_a = option_a
|
||||||
|
self.option_b = option_b
|
||||||
|
self.option_c = option_c
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_arguments(cls, parser):
|
||||||
|
parser.add_argument('--option_a', '-a', action='store', dest='option_a', default='1'),
|
||||||
|
parser.add_argument('--option_b', '-b', action='store', dest='option_b', default='2'),
|
||||||
|
parser.add_argument('--option_c', '-c', action='store', dest='option_c', default='3'),
|
||||||
|
|
||||||
|
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
||||||
|
print("%s:%s:%s" % (self.option_a, self.option_b, self.option_c))
|
|
@ -151,30 +151,11 @@ class ManageCommandTests(unittest.TestCase):
|
||||||
testrunner='test_runner.NonExistentRunner')
|
testrunner='test_runner.NonExistentRunner')
|
||||||
|
|
||||||
|
|
||||||
class CustomOptionsTestRunner(DiscoverRunner):
|
|
||||||
|
|
||||||
def __init__(self, verbosity=1, interactive=True, failfast=True, option_a=None, option_b=None, option_c=None, **kwargs):
|
|
||||||
super(CustomOptionsTestRunner, self).__init__(verbosity=verbosity, interactive=interactive,
|
|
||||||
failfast=failfast)
|
|
||||||
self.option_a = option_a
|
|
||||||
self.option_b = option_b
|
|
||||||
self.option_c = option_c
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_arguments(cls, parser):
|
|
||||||
parser.add_argument('--option_a', '-a', action='store', dest='option_a', default='1'),
|
|
||||||
parser.add_argument('--option_b', '-b', action='store', dest='option_b', default='2'),
|
|
||||||
parser.add_argument('--option_c', '-c', action='store', dest='option_c', default='3'),
|
|
||||||
|
|
||||||
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
|
||||||
print("%s:%s:%s" % (self.option_a, self.option_b, self.option_c))
|
|
||||||
|
|
||||||
|
|
||||||
class CustomTestRunnerOptionsTests(AdminScriptTestCase):
|
class CustomTestRunnerOptionsTests(AdminScriptTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
settings = {
|
settings = {
|
||||||
'TEST_RUNNER': '\'test_runner.tests.CustomOptionsTestRunner\'',
|
'TEST_RUNNER': '\'test_runner.runner.CustomOptionsTestRunner\'',
|
||||||
}
|
}
|
||||||
self.write_settings('settings.py', sdict=settings)
|
self.write_settings('settings.py', sdict=settings)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue