Refactored listing template subdirectories in apps.

This change has the nice side effect of removing code that ran at import
time and depended on the app registry at module level -- a notorious
cause of AppRegistryNotReady exceptions.
This commit is contained in:
Aymeric Augustin 2014-11-10 21:04:04 +01:00
parent cd7146debe
commit d58597a7b8
3 changed files with 34 additions and 27 deletions

View File

@ -4,38 +4,16 @@ packages.
""" """
import io import io
import os
import sys
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation from django.core.exceptions import SuspiciousFileOperation
from django.template.base import TemplateDoesNotExist from django.template.base import TemplateDoesNotExist
from django.template.utils import get_app_template_dirs
from django.utils._os import safe_join from django.utils._os import safe_join
from django.utils import six
from .base import Loader as BaseLoader from .base import Loader as BaseLoader
def calculate_app_template_dirs():
if six.PY2:
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
app_template_dirs = []
for app_config in apps.get_app_configs():
if not app_config.path:
continue
template_dir = os.path.join(app_config.path, 'templates')
if os.path.isdir(template_dir):
if six.PY2:
template_dir = template_dir.decode(fs_encoding)
app_template_dirs.append(template_dir)
return tuple(app_template_dirs)
# At compile time, cache the directories to search.
app_template_dirs = calculate_app_template_dirs()
class Loader(BaseLoader): class Loader(BaseLoader):
is_usable = True is_usable = True
@ -46,7 +24,7 @@ class Loader(BaseLoader):
template dirs are excluded from the result set, for security reasons. template dirs are excluded from the result set, for security reasons.
""" """
if not template_dirs: if not template_dirs:
template_dirs = app_template_dirs template_dirs = get_app_template_dirs('templates')
for template_dir in template_dirs: for template_dir in template_dirs:
try: try:
yield safe_join(template_dir, template_name) yield safe_join(template_dir, template_name)

29
django/template/utils.py Normal file
View File

@ -0,0 +1,29 @@
import os
import sys
from django.apps import apps
from django.utils import lru_cache
from django.utils import six
@lru_cache.lru_cache()
def get_app_template_dirs(dirname):
"""
Return an iterable of paths of directories to load app templates from.
dirname is the name of the subdirectory containing templates inside
installed applications.
"""
if six.PY2:
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
template_dirs = []
for app_config in apps.get_app_configs():
if not app_config.path:
continue
template_dir = os.path.join(app_config.path, dirname)
if os.path.isdir(template_dir):
if six.PY2:
template_dir = template_dir.decode(fs_encoding)
template_dirs.append(template_dir)
# Immutable return value because it will be cached and shared by callers.
return tuple(template_dirs)

View File

@ -39,9 +39,9 @@ def update_installed_apps(**kwargs):
# Rebuild templatetags module cache. # Rebuild templatetags module cache.
from django.template import base as mod from django.template import base as mod
mod.templatetags_modules = [] mod.templatetags_modules = []
# Rebuild app_template_dirs cache. # Rebuild get_app_template_dirs cache.
from django.template.loaders import app_directories as mod from django.template.utils import get_app_template_dirs
mod.app_template_dirs = mod.calculate_app_template_dirs() get_app_template_dirs.cache_clear()
# Rebuild translations cache. # Rebuild translations cache.
from django.utils.translation import trans_real from django.utils.translation import trans_real
trans_real._translations = {} trans_real._translations = {}