Wiped get_commands() cache when INSTALLED_APPS changes.

Refs #21018, #21688.
This commit is contained in:
Aymeric Augustin 2014-01-01 18:01:06 +01:00
parent 8a2f304a79
commit f17d00278e
2 changed files with 28 additions and 29 deletions

View File

@ -11,15 +11,12 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError, handle_default_options from django.core.management.base import BaseCommand, CommandError, handle_default_options
from django.core.management.color import color_style from django.core.management.color import color_style
from django.utils import lru_cache
from django.utils import six from django.utils import six
# For backwards compatibility: get_version() used to be in this module. # For backwards compatibility: get_version() used to be in this module.
from django import get_version from django import get_version
# A cache of loaded commands, so that call_command
# doesn't have to reload every time it's called.
_commands = None
def find_commands(management_dir): def find_commands(management_dir):
""" """
@ -85,6 +82,7 @@ def load_command_class(app_name, name):
return module.Command() return module.Command()
@lru_cache.lru_cache(maxsize=None)
def get_commands(): def get_commands():
""" """
Returns a dictionary mapping command names to their callback applications. Returns a dictionary mapping command names to their callback applications.
@ -107,34 +105,32 @@ def get_commands():
The dictionary is cached on the first call and reused on subsequent The dictionary is cached on the first call and reused on subsequent
calls. calls.
""" """
global _commands commands = {name: 'django.core' for name in find_commands(__path__[0])}
if _commands is None:
_commands = dict((name, 'django.core') for name in find_commands(__path__[0]))
# Find the installed apps # Find the installed apps
try:
settings.INSTALLED_APPS
except ImproperlyConfigured:
# Still useful for commands that do not require functional
# settings, like startproject or help.
app_names = []
else:
# Setup Django outside of the try/except block to avoid catching
# ImproperlyConfigured errors that aren't caused by the absence of
# a settings module.
django.setup()
app_configs = apps.get_app_configs()
app_names = [app_config.name for app_config in app_configs]
# Find and load the management module for each installed app.
for app_name in app_names:
try: try:
settings.INSTALLED_APPS path = find_management_module(app_name)
except ImproperlyConfigured: commands.update({name: app_name for name in find_commands(path)})
# Still useful for commands that do not require functional except ImportError:
# settings, like startproject or help. pass # No management module - ignore this app
app_names = []
else:
# Setup Django outside of the try/except block to avoid catching
# ImproperlyConfigured errors that aren't caused by the absence of
# a settings module.
django.setup()
app_configs = apps.get_app_configs()
app_names = [app_config.name for app_config in app_configs]
# Find and load the management module for each installed app. return commands
for app_name in app_names:
try:
path = find_management_module(app_name)
_commands.update(dict((name, app_name) for name in find_commands(path)))
except ImportError:
pass # No management module - ignore this app
return _commands
def call_command(name, *args, **options): def call_command(name, *args, **options):

View File

@ -39,6 +39,9 @@ def update_installed_apps(**kwargs):
# Rebuild templatetags module cache. # Rebuild templatetags module cache.
from django.template import base from django.template import base
base.templatetags_modules[:] = [] base.templatetags_modules[:] = []
# Rebuild management commands cache
from django.core.management import get_commands
get_commands.cache_clear()
@receiver(setting_changed) @receiver(setting_changed)