Commit 4ea43ac9 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Supported multiple template engines in get_template and select_template.

This commit changes the return type of these two functions. Instead of
returning a django.template.Template they return a backend-specific
Template class that must implement render(self, context).
parent 6854998c
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ from __future__ import absolute_import

from django.conf import settings
from django.template.context import Context, RequestContext
from django.template.engine import Engine
from django.template.engine import _dirs_undefined, Engine


from .base import BaseEngine
@@ -24,8 +24,8 @@ class DjangoTemplates(BaseEngine):
    def from_string(self, template_code):
        return Template(self.engine.from_string(template_code))

    def get_template(self, template_name):
        return Template(self.engine.get_template(template_name))
    def get_template(self, template_name, dirs=_dirs_undefined):
        return Template(self.engine.get_template(template_name, dirs))


class Template(object):
@@ -33,7 +33,15 @@ class Template(object):
    def __init__(self, template):
        self.template = template

    @property
    def origin(self):
        # TODO: define the Origin API. For now simply forwarding to the
        #       underlying Template preserves backwards-compatibility.
        return self.template.origin

    def render(self, context=None, request=None):
        # TODO: require context to be a dict -- through a deprecation path?
        if not isinstance(context, Context):
            if request is None:
                context = Context(context)
            else:
+2 −0
Original line number Diff line number Diff line
@@ -1266,6 +1266,8 @@ class Library(object):
                    if not getattr(self, 'nodelist', False):
                        if isinstance(file_name, Template):
                            t = file_name
                        elif isinstance(getattr(file_name, 'template', None), Template):
                            t = file_name.template
                        elif not isinstance(file_name, six.string_types) and is_iterable(file_name):
                            t = context.engine.select_template(file_name)
                        else:
+62 −6
Original line number Diff line number Diff line
@@ -2,8 +2,10 @@ import warnings

from django.utils.deprecation import RemovedInDjango20Warning

from .base import Origin
from .engine import Engine
from . import engines
from .backends.django import DjangoTemplates
from .base import Origin, TemplateDoesNotExist
from .engine import _dirs_undefined, Engine


class LoaderOrigin(Origin):
@@ -19,8 +21,62 @@ def find_template(*args, **kwargs):
    return Engine.get_default().find_template(*args, **kwargs)


def get_template(*args, **kwargs):
    return Engine.get_default().get_template(*args, **kwargs)
def get_template(template_name, dirs=_dirs_undefined, using=None):
    """
    Loads and returns a template for the given name.

    Raises TemplateDoesNotExist if no such template exists.
    """
    engines = _engine_list(using)
    for engine in engines:
        try:
            # This is required for deprecating the dirs argument. Simply
            # return engine.get_template(template_name) in Django 2.0.
            if isinstance(engine, DjangoTemplates):
                return engine.get_template(template_name, dirs)
            elif dirs is not _dirs_undefined:
                warnings.warn(
                    "Skipping template backend %s because its get_template "
                    "method doesn't support the dirs argument." % engine.name,
                    stacklevel=2)
            else:
                return engine.get_template(template_name)
        except TemplateDoesNotExist:
            pass

    raise TemplateDoesNotExist(template_name)


def select_template(template_name_list, dirs=_dirs_undefined, using=None):
    """
    Loads and returns a template for one of the given names.

    Tries names in order and returns the first template found.

    Raises TemplateDoesNotExist if no such template exists.
    """
    engines = _engine_list(using)
    for template_name in template_name_list:
        for engine in engines:
            try:
                # This is required for deprecating the dirs argument. Simply
                # use engine.get_template(template_name) in Django 2.0.
                if isinstance(engine, DjangoTemplates):
                    return engine.get_template(template_name, dirs)
                elif dirs is not _dirs_undefined:
                    warnings.warn(
                        "Skipping template backend %s because its get_template "
                        "method doesn't support the dirs argument." % engine.name,
                        stacklevel=2)
                else:
                    return engine.get_template(template_name)
            except TemplateDoesNotExist:
                pass

    if template_name_list:
        raise TemplateDoesNotExist(', '.join(template_name_list))
    else:
        raise TemplateDoesNotExist("No template names provided")


def get_template_from_string(*args, **kwargs):
@@ -31,8 +87,8 @@ def render_to_string(*args, **kwargs):
    return Engine.get_default().render_to_string(*args, **kwargs)


def select_template(*args, **kwargs):
    return Engine.get_default().select_template(*args, **kwargs)
def _engine_list(using=None):
    return engines.all() if using is None else [engines[using]]


# This line must remain at the bottom to avoid import loops.
+3 −3
Original line number Diff line number Diff line
@@ -85,7 +85,7 @@ class TemplateLoaderTests(SimpleTestCase):
        # We also rely on the fact the file system and app directories loaders
        # both inherit the load_template method from the base Loader class, so
        # we only need to test one of them.
        template = loader.get_template(load_name)
        template = loader.get_template(load_name).template
        template_name = template.nodelist[0].source[0].name
        self.assertTrue(template_name.endswith(load_name),
            'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
@@ -100,12 +100,12 @@ class TemplateLoaderTests(SimpleTestCase):
        load_name = 'login.html'

        # Test the cached loader separately since it overrides load_template.
        template = loader.get_template(load_name)
        template = loader.get_template(load_name).template
        template_name = template.nodelist[0].source[0].name
        self.assertTrue(template_name.endswith(load_name),
            'Template loaded through cached loader has incorrect name for debug page: %s' % template_name)

        template = loader.get_template(load_name)
        template = loader.get_template(load_name).template
        template_name = template.nodelist[0].source[0].name
        self.assertTrue(template_name.endswith(load_name),
            'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name)