Added initial support for loading template engines.

This commit is contained in:
Aymeric Augustin 2014-11-11 16:32:19 +01:00
parent b19693e6d8
commit 1acfd624d6
6 changed files with 230 additions and 2 deletions

View File

@ -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'

View File

@ -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/

View File

@ -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')

View File

@ -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()

View File

@ -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()

View File

@ -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`