Added initial support for loading template engines.
This commit is contained in:
parent
b19693e6d8
commit
1acfd624d6
|
@ -230,6 +230,8 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
# Output to use in template system for invalid (e.g. misspelled) variables.
|
# Output to use in template system for invalid (e.g. misspelled) variables.
|
||||||
TEMPLATE_STRING_IF_INVALID = ''
|
TEMPLATE_STRING_IF_INVALID = ''
|
||||||
|
|
||||||
|
TEMPLATES = []
|
||||||
|
|
||||||
# Default email address to use for various automated correspondence from
|
# Default email address to use for various automated correspondence from
|
||||||
# the site managers.
|
# the site managers.
|
||||||
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
|
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
|
||||||
|
|
|
@ -53,6 +53,25 @@ MIDDLEWARE_CLASSES = (
|
||||||
|
|
||||||
ROOT_URLCONF = '{{ project_name }}.urls'
|
ROOT_URLCONF = '{{ project_name }}.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.i18n',
|
||||||
|
'django.core.context_processors.tz',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.static',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = '{{ project_name }}.wsgi.application'
|
WSGI_APPLICATION = '{{ project_name }}.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +85,7 @@ DATABASES = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/
|
# https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
### Multiple Template Engines
|
||||||
|
|
||||||
|
from .utils import EngineHandler
|
||||||
|
|
||||||
|
|
||||||
|
engines = EngineHandler()
|
||||||
|
|
||||||
|
__all__ = ('engines',)
|
||||||
|
|
||||||
|
|
||||||
|
### Django Template Language
|
||||||
|
|
||||||
# Public exceptions
|
# Public exceptions
|
||||||
from .base import (TemplateDoesNotExist, TemplateSyntaxError, # NOQA
|
from .base import (TemplateDoesNotExist, TemplateSyntaxError, # NOQA
|
||||||
VariableDoesNotExist)
|
VariableDoesNotExist)
|
||||||
|
@ -14,4 +26,4 @@ from .base import resolve_variable # NOQA
|
||||||
from .base import Library # NOQA
|
from .base import Library # NOQA
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('Template', 'Context', 'RequestContext')
|
__all__ += ('Template', 'Context', 'RequestContext')
|
||||||
|
|
|
@ -1,9 +1,107 @@
|
||||||
|
from collections import Counter, OrderedDict
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils import lru_cache
|
from django.utils import lru_cache
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.module_loading import import_string
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidTemplateEngineError(ImproperlyConfigured):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EngineHandler(object):
|
||||||
|
def __init__(self, templates=None):
|
||||||
|
"""
|
||||||
|
templates is an optional list of template engine definitions
|
||||||
|
(structured like settings.TEMPLATES).
|
||||||
|
"""
|
||||||
|
self._templates = templates
|
||||||
|
self._engines = {}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def templates(self):
|
||||||
|
if self._templates is None:
|
||||||
|
self._templates = settings.TEMPLATES
|
||||||
|
|
||||||
|
if not self._templates:
|
||||||
|
warnings.warn(
|
||||||
|
"You haven't defined a TEMPLATES setting. You must do so "
|
||||||
|
"before upgrading to Django 2.0. Otherwise Django will be "
|
||||||
|
"unable to load templates.", RemovedInDjango20Warning)
|
||||||
|
self._templates = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': settings.TEMPLATE_DIRS,
|
||||||
|
'OPTIONS': {
|
||||||
|
'allowed_include_roots': settings.ALLOWED_INCLUDE_ROOTS,
|
||||||
|
'context_processors': settings.TEMPLATE_CONTEXT_PROCESSORS,
|
||||||
|
'loaders': settings.TEMPLATE_LOADERS,
|
||||||
|
'string_if_invalid': settings.TEMPLATE_STRING_IF_INVALID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
templates = OrderedDict()
|
||||||
|
for tpl in self._templates:
|
||||||
|
tpl = tpl.copy()
|
||||||
|
try:
|
||||||
|
# This will raise an exception if 'BACKEND' doesn't exist or
|
||||||
|
# isn't a string containing at least one dot.
|
||||||
|
default_name = tpl['BACKEND'].rsplit('.', 2)[-2]
|
||||||
|
except Exception:
|
||||||
|
invalid_backend = tpl.get('BACKEND', '<not defined>')
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
"Invalid BACKEND for a template engine: {}. Check "
|
||||||
|
"your TEMPLATES setting.".format(invalid_backend))
|
||||||
|
|
||||||
|
tpl.setdefault('NAME', default_name)
|
||||||
|
tpl.setdefault('DIRS', [])
|
||||||
|
tpl.setdefault('APP_DIRS', False)
|
||||||
|
tpl.setdefault('OPTIONS', {})
|
||||||
|
|
||||||
|
templates[tpl['NAME']] = tpl
|
||||||
|
|
||||||
|
counts = Counter(list(templates))
|
||||||
|
duplicates = [alias for alias, count in counts.most_common() if count > 1]
|
||||||
|
if duplicates:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
"Template engine aliases aren't unique, duplicates: {}. "
|
||||||
|
"Set a unique NAME for each engine in settings.TEMPLATES."
|
||||||
|
.format(", ".join(duplicates)))
|
||||||
|
|
||||||
|
return templates
|
||||||
|
|
||||||
|
def __getitem__(self, alias):
|
||||||
|
try:
|
||||||
|
return self._engines[alias]
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
params = self.templates[alias]
|
||||||
|
except KeyError:
|
||||||
|
raise InvalidTemplateEngineError(
|
||||||
|
"Could not find config for '{}' "
|
||||||
|
"in settings.TEMPLATES".format(alias))
|
||||||
|
|
||||||
|
backend = params.pop('BACKEND')
|
||||||
|
engine_cls = import_string(backend)
|
||||||
|
engine = engine_cls(params)
|
||||||
|
|
||||||
|
self._engines[alias] = engine
|
||||||
|
return engine
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.templates)
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
return [self[alias] for alias in self]
|
||||||
|
|
||||||
|
|
||||||
@lru_cache.lru_cache()
|
@lru_cache.lru_cache()
|
||||||
|
|
|
@ -84,8 +84,9 @@ def clear_routers_cache(**kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(setting_changed)
|
@receiver(setting_changed)
|
||||||
def reset_default_template_engine(**kwargs):
|
def reset_template_engines(**kwargs):
|
||||||
if kwargs['setting'] in {
|
if kwargs['setting'] in {
|
||||||
|
'TEMPLATES',
|
||||||
'TEMPLATE_DIRS',
|
'TEMPLATE_DIRS',
|
||||||
'ALLOWED_INCLUDE_ROOTS',
|
'ALLOWED_INCLUDE_ROOTS',
|
||||||
'TEMPLATE_CONTEXT_PROCESSORS',
|
'TEMPLATE_CONTEXT_PROCESSORS',
|
||||||
|
@ -93,7 +94,15 @@ def reset_default_template_engine(**kwargs):
|
||||||
'TEMPLATE_LOADERS',
|
'TEMPLATE_LOADERS',
|
||||||
'TEMPLATE_STRING_IF_INVALID',
|
'TEMPLATE_STRING_IF_INVALID',
|
||||||
'FILE_CHARSET',
|
'FILE_CHARSET',
|
||||||
|
'INSTALLED_APPS',
|
||||||
}:
|
}:
|
||||||
|
from django.template import engines
|
||||||
|
try:
|
||||||
|
del engines.templates
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
engines._templates = None
|
||||||
|
engines._engines = {}
|
||||||
from django.template.engine import Engine
|
from django.template.engine import Engine
|
||||||
Engine.get_default.cache_clear()
|
Engine.get_default.cache_clear()
|
||||||
|
|
||||||
|
|
|
@ -2288,6 +2288,92 @@ will still be printed, but will not prevent management commands from running.
|
||||||
|
|
||||||
See also the :doc:`/ref/checks` documentation.
|
See also the :doc:`/ref/checks` documentation.
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES
|
||||||
|
|
||||||
|
TEMPLATES
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
Default:: ``[]`` (Empty list)
|
||||||
|
|
||||||
|
A list containing the settings for all template engines to be used with
|
||||||
|
Django. Each item of the list is a dictionary containing the options for an
|
||||||
|
individual engine.
|
||||||
|
|
||||||
|
Here's a simple setup that tells the Django template engine to load templates
|
||||||
|
from the ``templates`` subdirectories inside installed applications::
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'APP_DIRS': True,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
The following options are available for all backends.
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES-BACKEND
|
||||||
|
|
||||||
|
BACKEND
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
Default: not defined
|
||||||
|
|
||||||
|
The template backend to use. The built-in template backends are:
|
||||||
|
|
||||||
|
* ``'django.template.backends.django.DjangoTemplates'``
|
||||||
|
* ``'django.template.backends.jinja2.Jinja2'``
|
||||||
|
|
||||||
|
You can use a template backend that doesn't ship with Django by setting
|
||||||
|
``BACKEND`` to a fully-qualified path (i.e. ``'mypackage.whatever.Backend'``).
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES-NAME
|
||||||
|
|
||||||
|
NAME
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Default: see below
|
||||||
|
|
||||||
|
The alias for this particular template engine. It's an identifier that allows
|
||||||
|
selecting an engine for rendering. Aliases must be unique across all
|
||||||
|
configured template engines.
|
||||||
|
|
||||||
|
It defaults to the name of the module defining the engine class, i.e. the
|
||||||
|
next to last piece of :setting:`BACKEND <TEMPLATES-BACKEND>`, when it isn't
|
||||||
|
provided. For example if the backend is ``'mypackage.whatever.Backend'`` then
|
||||||
|
its default name is ``'whatever'``.
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES-DIRS
|
||||||
|
|
||||||
|
DIRS
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Default:: ``[]`` (Empty list)
|
||||||
|
|
||||||
|
Directories where the engine should look for template source files, in search
|
||||||
|
order.
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES-APP_DIRS
|
||||||
|
|
||||||
|
APP_DIRS
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Default:: ``False``
|
||||||
|
|
||||||
|
Whether the engine should look for template source files inside installed
|
||||||
|
applications.
|
||||||
|
|
||||||
|
.. setting:: TEMPLATES-OPTIONS
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
Default:: ``{}`` (Empty dict)
|
||||||
|
|
||||||
|
Extra parameters to pass to the template backend. Available parameters vary
|
||||||
|
depending on the template backend.
|
||||||
|
|
||||||
.. setting:: TEMPLATE_CONTEXT_PROCESSORS
|
.. setting:: TEMPLATE_CONTEXT_PROCESSORS
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS
|
TEMPLATE_CONTEXT_PROCESSORS
|
||||||
|
@ -3327,6 +3413,7 @@ Serialization
|
||||||
Templates
|
Templates
|
||||||
---------
|
---------
|
||||||
* :setting:`ALLOWED_INCLUDE_ROOTS`
|
* :setting:`ALLOWED_INCLUDE_ROOTS`
|
||||||
|
* :setting:`TEMPLATES`
|
||||||
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
|
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
|
||||||
* :setting:`TEMPLATE_DEBUG`
|
* :setting:`TEMPLATE_DEBUG`
|
||||||
* :setting:`TEMPLATE_DIRS`
|
* :setting:`TEMPLATE_DIRS`
|
||||||
|
|
Loading…
Reference in New Issue