Commit 2133f315 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Fixed #24168 -- Allowed selecting a template engine in a few APIs.

Specifically in rendering shortcuts, template responses, and class-based
views that return template responses.

Also added a test for render_to_response(status=...) which was missing
from fdbfc980.

Thanks Tim and Carl for the review.
parent a5354185
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ from django.utils.functional import Promise
def render_to_response(template_name, context=None,
                       context_instance=_context_instance_undefined,
                       content_type=None, status=None, dirs=_dirs_undefined,
                       dictionary=_dictionary_undefined):
                       dictionary=_dictionary_undefined, using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
@@ -34,12 +34,13 @@ def render_to_response(template_name, context=None,
            and dirs is _dirs_undefined
            and dictionary is _dictionary_undefined):
        # No deprecated arguments were passed - use the new code path
        content = loader.render_to_string(template_name, context)
        content = loader.render_to_string(template_name, context, using=using)

    else:
        # Some deprecated arguments were passed - use the legacy code path
        content = loader.render_to_string(
            template_name, context, context_instance, dirs, dictionary)
            template_name, context, context_instance, dirs, dictionary,
            using=using)

    return HttpResponse(content, content_type, status)

@@ -47,7 +48,8 @@ def render_to_response(template_name, context=None,
def render(request, template_name, context=None,
           context_instance=_context_instance_undefined,
           content_type=None, status=None, current_app=_current_app_undefined,
           dirs=_dirs_undefined, dictionary=_dictionary_undefined):
           dirs=_dirs_undefined, dictionary=_dictionary_undefined,
           using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
@@ -59,7 +61,8 @@ def render(request, template_name, context=None,
            and dictionary is _dictionary_undefined):
        # No deprecated arguments were passed - use the new code path
        # In Django 2.0, request should become a positional argument.
        content = loader.render_to_string(template_name, context, request=request)
        content = loader.render_to_string(
            template_name, context, request=request, using=using)

    else:
        # Some deprecated arguments were passed - use the legacy code path
@@ -80,7 +83,8 @@ def render(request, template_name, context=None,
                context_instance._current_app = current_app

        content = loader.render_to_string(
            template_name, context, context_instance, dirs, dictionary)
            template_name, context, context_instance, dirs, dictionary,
            using=using)

    return HttpResponse(content, content_type, status)

+8 −5
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ class SimpleTemplateResponse(HttpResponse):
    rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks']

    def __init__(self, template, context=None, content_type=None, status=None,
                 charset=None):
                 charset=None, using=None):
        if isinstance(template, Template):
            warnings.warn(
                "{}'s template argument cannot be a django.template.Template "
@@ -31,6 +31,8 @@ class SimpleTemplateResponse(HttpResponse):
        self.template_name = template
        self.context_data = context

        self.using = using

        self._post_render_callbacks = []

        # _request stores the current request object in subclasses that know
@@ -73,9 +75,9 @@ class SimpleTemplateResponse(HttpResponse):
    def resolve_template(self, template):
        "Accepts a template object, path-to-template or list of paths"
        if isinstance(template, (list, tuple)):
            return loader.select_template(template)
            return loader.select_template(template, using=self.using)
        elif isinstance(template, six.string_types):
            return loader.get_template(template)
            return loader.get_template(template, using=self.using)
        else:
            return template

@@ -189,7 +191,8 @@ class TemplateResponse(SimpleTemplateResponse):
    rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request', '_current_app']

    def __init__(self, request, template, context=None, content_type=None,
            status=None, current_app=_current_app_undefined, charset=None):
            status=None, current_app=_current_app_undefined, charset=None,
            using=None):
        # As a convenience we'll allow callers to provide current_app without
        # having to avoid needing to create the RequestContext directly
        if current_app is not _current_app_undefined:
@@ -199,5 +202,5 @@ class TemplateResponse(SimpleTemplateResponse):
                RemovedInDjango20Warning, stacklevel=2)
            request.current_app = current_app
        super(TemplateResponse, self).__init__(
            template, context, content_type, status, charset)
            template, context, content_type, status, charset, using)
        self._request = request
+23 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ import logging
import re
import sys
import time
from unittest import skipUnless
from unittest import skipIf, skipUnless
import warnings
from functools import wraps
from xml.dom.minidom import parseString, Node
@@ -20,6 +20,11 @@ from django.utils import six
from django.utils.encoding import force_str
from django.utils.translation import deactivate

try:
    import jinja2
except ImportError:
    jinja2 = None


__all__ = (
    'Approximate', 'ContextList', 'get_runner',
@@ -573,3 +578,20 @@ def freeze_time(t):
        yield
    finally:
        time.time = _real_time


def require_jinja2(test_func):
    """
    Decorator to enable a Jinja2 template engine in addition to the regular
    Django template engine for a test or skip it if Jinja2 isn't available.
    """
    test_func = skipIf(jinja2 is None, "this test requires jinja2")(test_func)
    test_func = override_settings(TEMPLATES=[{
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'APP_DIRS': True,
    }, {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'APP_DIRS': True,
        'OPTIONS': {'keep_trailing_newline': True},
    }])(test_func)
    return test_func
+2 −0
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ class TemplateResponseMixin(object):
    A mixin that can be used to render a template.
    """
    template_name = None
    template_engine = None
    response_class = TemplateResponse
    content_type = None

@@ -130,6 +131,7 @@ class TemplateResponseMixin(object):
            request=self.request,
            template=self.get_template_names(),
            context=context,
            using=self.template_engine,
            **response_kwargs
        )

+14 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ TemplateView
* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]

**Methods**
@@ -89,6 +90,7 @@ DetailView
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
@@ -124,6 +126,7 @@ ListView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`

@@ -155,6 +158,7 @@ FormView
* :attr:`~django.views.generic.edit.FormMixin.prefix` [:meth:`~django.views.generic.edit.FormMixin.get_prefix`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]

**Methods**
@@ -191,6 +195,7 @@ CreateView
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
@@ -232,6 +237,7 @@ UpdateView
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
@@ -269,6 +275,7 @@ DeleteView
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.DeletionMixin.success_url` [:meth:`~django.views.generic.edit.DeletionMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
@@ -308,6 +315,7 @@ ArchiveIndexView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`

@@ -346,6 +354,7 @@ YearArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -387,6 +396,7 @@ MonthArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -428,6 +438,7 @@ WeekArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.WeekMixin.week` [:meth:`~django.views.generic.dates.WeekMixin.get_week`]
@@ -473,6 +484,7 @@ DayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -520,6 +532,7 @@ TodayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -565,6 +578,7 @@ DateDetailView
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_engine`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
Loading