Fixed #21018 -- Reversed precedence order for management commands.
This commit is contained in:
parent
f17d00278e
commit
0ce945a671
|
@ -120,7 +120,7 @@ def get_commands():
|
||||||
# a settings module.
|
# a settings module.
|
||||||
django.setup()
|
django.setup()
|
||||||
app_configs = apps.get_app_configs()
|
app_configs = apps.get_app_configs()
|
||||||
app_names = [app_config.name for app_config in app_configs]
|
app_names = [app_config.name for app_config in reversed(app_configs)]
|
||||||
|
|
||||||
# Find and load the management module for each installed app.
|
# Find and load the management module for each installed app.
|
||||||
for app_name in app_names:
|
for app_name in app_names:
|
||||||
|
|
|
@ -1319,6 +1319,10 @@ Django installation. Each string should be a dotted Python path to:
|
||||||
These rules apply regardless of whether :setting:`INSTALLED_APPS`
|
These rules apply regardless of whether :setting:`INSTALLED_APPS`
|
||||||
references application configuration classes on application packages.
|
references application configuration classes on application packages.
|
||||||
|
|
||||||
|
When several applications provide different versions of the same resource
|
||||||
|
(template, static file, management command, translation), the application
|
||||||
|
listed first in :setting:`INSTALLED_APPS` has precedence.
|
||||||
|
|
||||||
.. setting:: INTERNAL_IPS
|
.. setting:: INTERNAL_IPS
|
||||||
|
|
||||||
INTERNAL_IPS
|
INTERNAL_IPS
|
||||||
|
|
|
@ -697,6 +697,18 @@ following changes that take effect immediately:
|
||||||
* The ``only_installed`` argument of ``get_model`` and ``get_models`` no
|
* The ``only_installed`` argument of ``get_model`` and ``get_models`` no
|
||||||
longer exists, nor does the ``seed_cache`` argument of ``get_model``.
|
longer exists, nor does the ``seed_cache`` argument of ``get_model``.
|
||||||
|
|
||||||
|
Management commands and order of :setting:`INSTALLED_APPS`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When several applications provide management commands with the same name,
|
||||||
|
Django loads the command from the application that comes first in
|
||||||
|
:setting:`INSTALLED_APPS`. Previous versions loaded the command from the
|
||||||
|
applicatino that came last.
|
||||||
|
|
||||||
|
This brings discovery of management commands in line with other parts of
|
||||||
|
Django that rely on the order of :setting:`INSTALLED_APPS`, such as static
|
||||||
|
files, templates, and translations.
|
||||||
|
|
||||||
Behavior of ``LocMemCache`` regarding pickle errors
|
Behavior of ``LocMemCache`` regarding pickle errors
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
self.stdout.write('complex_app')
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
self.stdout.write('simple_app')
|
|
@ -25,7 +25,7 @@ from django.test.utils import str_prefix
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
from django.utils.six import StringIO
|
from django.utils.six import StringIO
|
||||||
from django.test import LiveServerTestCase
|
from django.test import LiveServerTestCase, TestCase
|
||||||
|
|
||||||
|
|
||||||
test_dir = os.path.realpath(os.path.join(os.environ['DJANGO_TEST_TEMP_DIR'], 'test_project'))
|
test_dir = os.path.realpath(os.path.join(os.environ['DJANGO_TEST_TEMP_DIR'], 'test_project'))
|
||||||
|
@ -1469,6 +1469,24 @@ class CommandTypes(AdminScriptTestCase):
|
||||||
self.assertOutput(out, str_prefix("EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
self.assertOutput(out, str_prefix("EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', %(_)s'1')]"))
|
||||||
|
|
||||||
|
|
||||||
|
class Discovery(TestCase):
|
||||||
|
|
||||||
|
def test_precedence(self):
|
||||||
|
"""
|
||||||
|
Apps listed first in INSTALLED_APPS have precendence.
|
||||||
|
"""
|
||||||
|
with self.settings(INSTALLED_APPS=['admin_scripts.complex_app',
|
||||||
|
'admin_scripts.simple_app']):
|
||||||
|
out = StringIO()
|
||||||
|
call_command('duplicate', stdout=out)
|
||||||
|
self.assertEqual(out.getvalue().strip(), 'complex_app')
|
||||||
|
with self.settings(INSTALLED_APPS=['admin_scripts.simple_app',
|
||||||
|
'admin_scripts.complex_app']):
|
||||||
|
out = StringIO()
|
||||||
|
call_command('duplicate', stdout=out)
|
||||||
|
self.assertEqual(out.getvalue().strip(), 'simple_app')
|
||||||
|
|
||||||
|
|
||||||
class ArgumentOrder(AdminScriptTestCase):
|
class ArgumentOrder(AdminScriptTestCase):
|
||||||
"""Tests for 2-stage argument parsing scheme.
|
"""Tests for 2-stage argument parsing scheme.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue