Fixed #4278 -- Added a dirs parameter to a few functions to override TEMPLATE_DIRS.
* django.template.loader.get_template() * django.template.loader.select_template() * django.shortcuts.render() * django.shortcuts.render_to_response() Thanks amcnabb for the suggestion.
This commit is contained in:
parent
893198509e
commit
2f0566fa61
|
@ -130,12 +130,12 @@ def find_template(name, dirs=None):
|
||||||
pass
|
pass
|
||||||
raise TemplateDoesNotExist(name)
|
raise TemplateDoesNotExist(name)
|
||||||
|
|
||||||
def get_template(template_name):
|
def get_template(template_name, dirs=None):
|
||||||
"""
|
"""
|
||||||
Returns a compiled Template object for the given template name,
|
Returns a compiled Template object for the given template name,
|
||||||
handling template inheritance recursively.
|
handling template inheritance recursively.
|
||||||
"""
|
"""
|
||||||
template, origin = find_template(template_name)
|
template, origin = find_template(template_name, dirs)
|
||||||
if not hasattr(template, 'render'):
|
if not hasattr(template, 'render'):
|
||||||
# template needs to be compiled
|
# template needs to be compiled
|
||||||
template = get_template_from_string(template, origin, template_name)
|
template = get_template_from_string(template, origin, template_name)
|
||||||
|
@ -148,7 +148,8 @@ def get_template_from_string(source, origin=None, name=None):
|
||||||
"""
|
"""
|
||||||
return Template(source, origin, name)
|
return Template(source, origin, name)
|
||||||
|
|
||||||
def render_to_string(template_name, dictionary=None, context_instance=None):
|
def render_to_string(template_name, dictionary=None, context_instance=None,
|
||||||
|
dirs=None):
|
||||||
"""
|
"""
|
||||||
Loads the given template_name and renders it with the given dictionary as
|
Loads the given template_name and renders it with the given dictionary as
|
||||||
context. The template_name may be a string to load a single template using
|
context. The template_name may be a string to load a single template using
|
||||||
|
@ -157,9 +158,9 @@ def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||||
"""
|
"""
|
||||||
dictionary = dictionary or {}
|
dictionary = dictionary or {}
|
||||||
if isinstance(template_name, (list, tuple)):
|
if isinstance(template_name, (list, tuple)):
|
||||||
t = select_template(template_name)
|
t = select_template(template_name, dirs)
|
||||||
else:
|
else:
|
||||||
t = get_template(template_name)
|
t = get_template(template_name, dirs)
|
||||||
if not context_instance:
|
if not context_instance:
|
||||||
return t.render(Context(dictionary))
|
return t.render(Context(dictionary))
|
||||||
# Add the dictionary to the context stack, ensuring it gets removed again
|
# Add the dictionary to the context stack, ensuring it gets removed again
|
||||||
|
@ -167,14 +168,14 @@ def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||||
with context_instance.push(dictionary):
|
with context_instance.push(dictionary):
|
||||||
return t.render(context_instance)
|
return t.render(context_instance)
|
||||||
|
|
||||||
def select_template(template_name_list):
|
def select_template(template_name_list, dirs=None):
|
||||||
"Given a list of template names, returns the first that can be loaded."
|
"Given a list of template names, returns the first that can be loaded."
|
||||||
if not template_name_list:
|
if not template_name_list:
|
||||||
raise TemplateDoesNotExist("No template names provided")
|
raise TemplateDoesNotExist("No template names provided")
|
||||||
not_found = []
|
not_found = []
|
||||||
for template_name in template_name_list:
|
for template_name in template_name_list:
|
||||||
try:
|
try:
|
||||||
return get_template(template_name)
|
return get_template(template_name, dirs)
|
||||||
except TemplateDoesNotExist as e:
|
except TemplateDoesNotExist as e:
|
||||||
if e.args[0] not in not_found:
|
if e.args[0] not in not_found:
|
||||||
not_found.append(e.args[0])
|
not_found.append(e.args[0])
|
||||||
|
|
|
@ -594,17 +594,31 @@ The Python API
|
||||||
|
|
||||||
``django.template.loader`` has two functions to load templates from files:
|
``django.template.loader`` has two functions to load templates from files:
|
||||||
|
|
||||||
.. function:: get_template(template_name)
|
.. function:: get_template(template_name[, dirs])
|
||||||
|
|
||||||
``get_template`` returns the compiled template (a ``Template`` object) for
|
``get_template`` returns the compiled template (a ``Template`` object) for
|
||||||
the template with the given name. If the template doesn't exist, it raises
|
the template with the given name. If the template doesn't exist, it raises
|
||||||
``django.template.TemplateDoesNotExist``.
|
``django.template.TemplateDoesNotExist``.
|
||||||
|
|
||||||
.. function:: select_template(template_name_list)
|
To override the :setting:`TEMPLATE_DIRS` setting, use the ``dirs``
|
||||||
|
parameter. The ``dirs`` parameter may be a tuple or list.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
The ``dirs`` parameter was added.
|
||||||
|
|
||||||
|
.. function:: select_template(template_name_list[, dirs])
|
||||||
|
|
||||||
``select_template`` is just like ``get_template``, except it takes a list
|
``select_template`` is just like ``get_template``, except it takes a list
|
||||||
of template names. Of the list, it returns the first template that exists.
|
of template names. Of the list, it returns the first template that exists.
|
||||||
|
|
||||||
|
To override the :setting:`TEMPLATE_DIRS` setting, use the ``dirs``
|
||||||
|
parameter. The ``dirs`` parameter may be a tuple or list.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
The ``dirs`` parameter was added.
|
||||||
|
|
||||||
For example, if you call ``get_template('story_detail.html')`` and have the
|
For example, if you call ``get_template('story_detail.html')`` and have the
|
||||||
above :setting:`TEMPLATE_DIRS` setting, here are the files Django will look for,
|
above :setting:`TEMPLATE_DIRS` setting, here are the files Django will look for,
|
||||||
in order:
|
in order:
|
||||||
|
|
|
@ -285,6 +285,14 @@ Templates
|
||||||
* ``TypeError`` exceptions are not longer silenced when raised during the
|
* ``TypeError`` exceptions are not longer silenced when raised during the
|
||||||
rendering of a template.
|
rendering of a template.
|
||||||
|
|
||||||
|
* The following functions now accept a ``dirs`` parameter which is a list or
|
||||||
|
tuple to override :setting:`TEMPLATE_DIRS`:
|
||||||
|
|
||||||
|
* :func:`django.template.loader.get_template()`
|
||||||
|
* :func:`django.template.loader.select_template()`
|
||||||
|
* :func:`django.shortcuts.render()`
|
||||||
|
* :func:`django.shortcuts.render_to_response()`
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake.
|
||||||
``render``
|
``render``
|
||||||
==========
|
==========
|
||||||
|
|
||||||
.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app])
|
.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app][, dirs])
|
||||||
|
|
||||||
Combines a given template with a given context dictionary and returns an
|
Combines a given template with a given context dictionary and returns an
|
||||||
:class:`~django.http.HttpResponse` object with that rendered text.
|
:class:`~django.http.HttpResponse` object with that rendered text.
|
||||||
|
@ -58,6 +58,13 @@ Optional arguments
|
||||||
:ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`
|
:ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
``dirs``
|
||||||
|
A tuple or list of values to override the :setting:`TEMPLATE_DIRS` setting.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
The ``dirs`` parameter was added.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -83,11 +90,19 @@ This example is equivalent to::
|
||||||
return HttpResponse(t.render(c),
|
return HttpResponse(t.render(c),
|
||||||
content_type="application/xhtml+xml")
|
content_type="application/xhtml+xml")
|
||||||
|
|
||||||
|
If you want to override the :setting:`TEMPLATE_DIRS` setting, use the
|
||||||
|
``dirs`` parameter::
|
||||||
|
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
def my_view(request):
|
||||||
|
# View code here...
|
||||||
|
return render(request, 'index.html', dirs=('custom_templates',))
|
||||||
|
|
||||||
``render_to_response``
|
``render_to_response``
|
||||||
======================
|
======================
|
||||||
|
|
||||||
.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type])
|
.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type][, dirs])
|
||||||
|
|
||||||
Renders a given template with a given context dictionary and returns an
|
Renders a given template with a given context dictionary and returns an
|
||||||
:class:`~django.http.HttpResponse` object with that rendered text.
|
:class:`~django.http.HttpResponse` object with that rendered text.
|
||||||
|
@ -125,6 +140,13 @@ Optional arguments
|
||||||
The MIME type to use for the resulting document. Defaults to the value of
|
The MIME type to use for the resulting document. Defaults to the value of
|
||||||
the :setting:`DEFAULT_CONTENT_TYPE` setting.
|
the :setting:`DEFAULT_CONTENT_TYPE` setting.
|
||||||
|
|
||||||
|
``dirs``
|
||||||
|
A tuple or list of values to override the :setting:`TEMPLATE_DIRS` setting.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
The ``dirs`` parameter was added.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -150,6 +172,15 @@ This example is equivalent to::
|
||||||
return HttpResponse(t.render(c),
|
return HttpResponse(t.render(c),
|
||||||
content_type="application/xhtml+xml")
|
content_type="application/xhtml+xml")
|
||||||
|
|
||||||
|
If you want to override the :setting:`TEMPLATE_DIRS` setting, use the
|
||||||
|
``dirs`` parameter::
|
||||||
|
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
|
||||||
|
def my_view(request):
|
||||||
|
# View code here...
|
||||||
|
return render_to_response('index.html', dirs=('custom_templates',))
|
||||||
|
|
||||||
``redirect``
|
``redirect``
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
spam eggs{{ obj }}
|
|
@ -174,8 +174,28 @@ class RenderToStringTest(unittest.TestCase):
|
||||||
'No template names provided$',
|
'No template names provided$',
|
||||||
loader.render_to_string, [])
|
loader.render_to_string, [])
|
||||||
|
|
||||||
|
|
||||||
def test_select_templates_from_empty_list(self):
|
def test_select_templates_from_empty_list(self):
|
||||||
six.assertRaisesRegex(self, TemplateDoesNotExist,
|
six.assertRaisesRegex(self, TemplateDoesNotExist,
|
||||||
'No template names provided$',
|
'No template names provided$',
|
||||||
loader.select_template, [])
|
loader.select_template, [])
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateDirsOverrideTest(unittest.TestCase):
|
||||||
|
|
||||||
|
dirs_tuple = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
|
||||||
|
dirs_list = list(dirs_tuple)
|
||||||
|
dirs_iter = (dirs_tuple, dirs_list)
|
||||||
|
|
||||||
|
def test_render_to_string(self):
|
||||||
|
for dirs in self.dirs_iter:
|
||||||
|
self.assertEqual(loader.render_to_string('test_dirs.html', dirs=dirs), 'spam eggs\n')
|
||||||
|
|
||||||
|
def test_get_template(self):
|
||||||
|
for dirs in self.dirs_iter:
|
||||||
|
template = loader.get_template('test_dirs.html', dirs=dirs)
|
||||||
|
self.assertEqual(template.render(Context({})), 'spam eggs\n')
|
||||||
|
|
||||||
|
def test_select_template(self):
|
||||||
|
for dirs in self.dirs_iter:
|
||||||
|
template = loader.select_template(['test_dirs.html'], dirs=dirs)
|
||||||
|
self.assertEqual(template.render(Context({})), 'spam eggs\n')
|
||||||
|
|
|
@ -48,10 +48,12 @@ urlpatterns += patterns('view_tests.views',
|
||||||
(r'^shortcuts/render_to_response/$', 'render_to_response_view'),
|
(r'^shortcuts/render_to_response/$', 'render_to_response_view'),
|
||||||
(r'^shortcuts/render_to_response/request_context/$', 'render_to_response_view_with_request_context'),
|
(r'^shortcuts/render_to_response/request_context/$', 'render_to_response_view_with_request_context'),
|
||||||
(r'^shortcuts/render_to_response/content_type/$', 'render_to_response_view_with_content_type'),
|
(r'^shortcuts/render_to_response/content_type/$', 'render_to_response_view_with_content_type'),
|
||||||
|
(r'^shortcuts/render_to_response/dirs/$', 'render_to_response_view_with_dirs'),
|
||||||
(r'^shortcuts/render/$', 'render_view'),
|
(r'^shortcuts/render/$', 'render_view'),
|
||||||
(r'^shortcuts/render/base_context/$', 'render_view_with_base_context'),
|
(r'^shortcuts/render/base_context/$', 'render_view_with_base_context'),
|
||||||
(r'^shortcuts/render/content_type/$', 'render_view_with_content_type'),
|
(r'^shortcuts/render/content_type/$', 'render_view_with_content_type'),
|
||||||
(r'^shortcuts/render/status/$', 'render_view_with_status'),
|
(r'^shortcuts/render/status/$', 'render_view_with_status'),
|
||||||
(r'^shortcuts/render/current_app/$', 'render_view_with_current_app'),
|
(r'^shortcuts/render/current_app/$', 'render_view_with_current_app'),
|
||||||
|
(r'^shortcuts/render/dirs/$', 'render_with_dirs'),
|
||||||
(r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
|
(r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
spam eggs
|
|
@ -27,6 +27,12 @@ class ShortcutTests(TestCase):
|
||||||
self.assertEqual(response.content, b'FOO.BAR..\n')
|
self.assertEqual(response.content, b'FOO.BAR..\n')
|
||||||
self.assertEqual(response['Content-Type'], 'application/x-rendertest')
|
self.assertEqual(response['Content-Type'], 'application/x-rendertest')
|
||||||
|
|
||||||
|
def test_render_to_response_with_dirs(self):
|
||||||
|
response = self.client.get('/shortcuts/render_to_response/dirs/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.content, b'spam eggs\n')
|
||||||
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
||||||
def test_render(self):
|
def test_render(self):
|
||||||
response = self.client.get('/shortcuts/render/')
|
response = self.client.get('/shortcuts/render/')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -55,5 +61,11 @@ class ShortcutTests(TestCase):
|
||||||
response = self.client.get('/shortcuts/render/current_app/')
|
response = self.client.get('/shortcuts/render/current_app/')
|
||||||
self.assertEqual(response.context.current_app, "foobar_app")
|
self.assertEqual(response.context.current_app, "foobar_app")
|
||||||
|
|
||||||
|
def test_render_with_dirs(self):
|
||||||
|
response = self.client.get('/shortcuts/render/dirs/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.content, b'spam eggs\n')
|
||||||
|
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
|
||||||
|
|
||||||
def test_render_with_current_app_conflict(self):
|
def test_render_with_current_app_conflict(self):
|
||||||
self.assertRaises(ValueError, self.client.get, '/shortcuts/render/current_app_conflict/')
|
self.assertRaises(ValueError, self.client.get, '/shortcuts/render/current_app_conflict/')
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied, SuspiciousOperation
|
from django.core.exceptions import PermissionDenied, SuspiciousOperation
|
||||||
|
@ -10,10 +11,12 @@ from django.template import Context, RequestContext, TemplateDoesNotExist
|
||||||
from django.views.debug import technical_500_response, SafeExceptionReporterFilter
|
from django.views.debug import technical_500_response, SafeExceptionReporterFilter
|
||||||
from django.views.decorators.debug import (sensitive_post_parameters,
|
from django.views.decorators.debug import (sensitive_post_parameters,
|
||||||
sensitive_variables)
|
sensitive_variables)
|
||||||
|
from django.utils._os import upath
|
||||||
from django.utils.log import getLogger
|
from django.utils.log import getLogger
|
||||||
|
|
||||||
from . import BrokenException, except_args
|
from . import BrokenException, except_args
|
||||||
|
|
||||||
|
dirs = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
|
||||||
|
|
||||||
|
|
||||||
def index_page(request):
|
def index_page(request):
|
||||||
|
@ -85,6 +88,9 @@ def render_to_response_view_with_content_type(request):
|
||||||
'bar': 'BAR',
|
'bar': 'BAR',
|
||||||
}, content_type='application/x-rendertest')
|
}, content_type='application/x-rendertest')
|
||||||
|
|
||||||
|
def render_to_response_view_with_dirs(request):
|
||||||
|
return render_to_response('render_dirs_test.html', dirs=dirs)
|
||||||
|
|
||||||
def render_view(request):
|
def render_view(request):
|
||||||
return render(request, 'debug/render_test.html', {
|
return render(request, 'debug/render_test.html', {
|
||||||
'foo': 'FOO',
|
'foo': 'FOO',
|
||||||
|
@ -123,6 +129,9 @@ def render_view_with_current_app_conflict(request):
|
||||||
'bar': 'BAR',
|
'bar': 'BAR',
|
||||||
}, current_app="foobar_app", context_instance=RequestContext(request))
|
}, current_app="foobar_app", context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
def render_with_dirs(request):
|
||||||
|
return render(request, 'render_dirs_test.html', dirs=dirs)
|
||||||
|
|
||||||
def raises_template_does_not_exist(request, path='i_dont_exist.html'):
|
def raises_template_does_not_exist(request, path='i_dont_exist.html'):
|
||||||
# We need to inspect the HTML generated by the fancy 500 debug view but
|
# We need to inspect the HTML generated by the fancy 500 debug view but
|
||||||
# the test client ignores it, so we send it explicitly.
|
# the test client ignores it, so we send it explicitly.
|
||||||
|
|
Loading…
Reference in New Issue