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.
|
||||
TEMPLATE_STRING_IF_INVALID = ''
|
||||
|
||||
TEMPLATES = []
|
||||
|
||||
# Default email address to use for various automated correspondence from
|
||||
# the site managers.
|
||||
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
|
||||
|
|
|
@ -53,6 +53,25 @@ MIDDLEWARE_CLASSES = (
|
|||
|
||||
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'
|
||||
|
||||
|
||||
|
@ -66,6 +85,7 @@ DATABASES = {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
# Internationalization
|
||||
# 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
|
||||
from .base import (TemplateDoesNotExist, TemplateSyntaxError, # NOQA
|
||||
VariableDoesNotExist)
|
||||
|
@ -14,4 +26,4 @@ from .base import resolve_variable # 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 sys
|
||||
import warnings
|
||||
|
||||
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 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()
|
||||
|
|
|
@ -84,8 +84,9 @@ def clear_routers_cache(**kwargs):
|
|||
|
||||
|
||||
@receiver(setting_changed)
|
||||
def reset_default_template_engine(**kwargs):
|
||||
def reset_template_engines(**kwargs):
|
||||
if kwargs['setting'] in {
|
||||
'TEMPLATES',
|
||||
'TEMPLATE_DIRS',
|
||||
'ALLOWED_INCLUDE_ROOTS',
|
||||
'TEMPLATE_CONTEXT_PROCESSORS',
|
||||
|
@ -93,7 +94,15 @@ def reset_default_template_engine(**kwargs):
|
|||
'TEMPLATE_LOADERS',
|
||||
'TEMPLATE_STRING_IF_INVALID',
|
||||
'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
|
||||
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.
|
||||
|
||||
.. 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
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS
|
||||
|
@ -3327,6 +3413,7 @@ Serialization
|
|||
Templates
|
||||
---------
|
||||
* :setting:`ALLOWED_INCLUDE_ROOTS`
|
||||
* :setting:`TEMPLATES`
|
||||
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
|
||||
* :setting:`TEMPLATE_DEBUG`
|
||||
* :setting:`TEMPLATE_DIRS`
|
||||
|
|
Loading…
Reference in New Issue