From 0ce945a67151acf2c58bc35a47f4c3d45ff30085 Mon Sep 17 00:00:00 2001
From: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Wed, 1 Jan 2014 18:05:12 +0100
Subject: [PATCH] Fixed #21018 -- Reversed precedence order for management
 commands.

---
 django/core/management/__init__.py            |  2 +-
 docs/ref/settings.txt                         |  4 ++++
 docs/releases/1.7.txt                         | 12 +++++++++++
 .../complex_app/management/__init__.py        |  0
 .../management/commands/__init__.py           |  0
 .../management/commands/duplicate.py          |  7 +++++++
 .../simple_app/management/__init__.py         |  0
 .../management/commands/__init__.py           |  0
 .../management/commands/duplicate.py          |  7 +++++++
 tests/admin_scripts/tests.py                  | 20 ++++++++++++++++++-
 10 files changed, 50 insertions(+), 2 deletions(-)
 create mode 100644 tests/admin_scripts/complex_app/management/__init__.py
 create mode 100644 tests/admin_scripts/complex_app/management/commands/__init__.py
 create mode 100644 tests/admin_scripts/complex_app/management/commands/duplicate.py
 create mode 100644 tests/admin_scripts/simple_app/management/__init__.py
 create mode 100644 tests/admin_scripts/simple_app/management/commands/__init__.py
 create mode 100644 tests/admin_scripts/simple_app/management/commands/duplicate.py

diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 1b9597ee3d..33cef35b4c 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -120,7 +120,7 @@ def get_commands():
         # a settings module.
         django.setup()
         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.
     for app_name in app_names:
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 62e7920c01..4181d152ef 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -1319,6 +1319,10 @@ Django installation. Each string should be a dotted Python path to:
     These rules apply regardless of whether :setting:`INSTALLED_APPS`
     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
 
 INTERNAL_IPS
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index b559445e7c..fced7a7508 100644
--- a/docs/releases/1.7.txt
+++ b/docs/releases/1.7.txt
@@ -697,6 +697,18 @@ following changes that take effect immediately:
 * The ``only_installed`` argument of ``get_model`` and ``get_models`` no
   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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/tests/admin_scripts/complex_app/management/__init__.py b/tests/admin_scripts/complex_app/management/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/admin_scripts/complex_app/management/commands/__init__.py b/tests/admin_scripts/complex_app/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/admin_scripts/complex_app/management/commands/duplicate.py b/tests/admin_scripts/complex_app/management/commands/duplicate.py
new file mode 100644
index 0000000000..11b183843f
--- /dev/null
+++ b/tests/admin_scripts/complex_app/management/commands/duplicate.py
@@ -0,0 +1,7 @@
+from django.core.management.base import NoArgsCommand
+
+
+class Command(NoArgsCommand):
+
+    def handle_noargs(self, **options):
+        self.stdout.write('complex_app')
diff --git a/tests/admin_scripts/simple_app/management/__init__.py b/tests/admin_scripts/simple_app/management/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/admin_scripts/simple_app/management/commands/__init__.py b/tests/admin_scripts/simple_app/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/admin_scripts/simple_app/management/commands/duplicate.py b/tests/admin_scripts/simple_app/management/commands/duplicate.py
new file mode 100644
index 0000000000..a451f3991c
--- /dev/null
+++ b/tests/admin_scripts/simple_app/management/commands/duplicate.py
@@ -0,0 +1,7 @@
+from django.core.management.base import NoArgsCommand
+
+
+class Command(NoArgsCommand):
+
+    def handle_noargs(self, **options):
+        self.stdout.write('simple_app')
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py
index 3587748c8e..e052a1a6d7 100644
--- a/tests/admin_scripts/tests.py
+++ b/tests/admin_scripts/tests.py
@@ -25,7 +25,7 @@ from django.test.utils import str_prefix
 from django.utils.encoding import force_text
 from django.utils._os import upath
 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'))
@@ -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')]"))
 
 
+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):
     """Tests for 2-stage argument parsing scheme.