Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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.
  • Loading branch information...
commit 2f0566fa61e13277364e3aef338fa5c143f5a704 1 parent 8931985
Berker Peksag authored September 16, 2013 timgraham committed September 18, 2013
15  django/template/loader.py
@@ -130,12 +130,12 @@ def find_template(name, dirs=None):
130 130
             pass
131 131
     raise TemplateDoesNotExist(name)
132 132
 
133  
-def get_template(template_name):
  133
+def get_template(template_name, dirs=None):
134 134
     """
135 135
     Returns a compiled Template object for the given template name,
136 136
     handling template inheritance recursively.
137 137
     """
138  
-    template, origin = find_template(template_name)
  138
+    template, origin = find_template(template_name, dirs)
139 139
     if not hasattr(template, 'render'):
140 140
         # template needs to be compiled
141 141
         template = get_template_from_string(template, origin, template_name)
@@ -148,7 +148,8 @@ def get_template_from_string(source, origin=None, name=None):
148 148
     """
149 149
     return Template(source, origin, name)
150 150
 
151  
-def render_to_string(template_name, dictionary=None, context_instance=None):
  151
+def render_to_string(template_name, dictionary=None, context_instance=None,
  152
+                     dirs=None):
152 153
     """
153 154
     Loads the given template_name and renders it with the given dictionary as
154 155
     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):
157 158
     """
158 159
     dictionary = dictionary or {}
159 160
     if isinstance(template_name, (list, tuple)):
160  
-        t = select_template(template_name)
  161
+        t = select_template(template_name, dirs)
161 162
     else:
162  
-        t = get_template(template_name)
  163
+        t = get_template(template_name, dirs)
163 164
     if not context_instance:
164 165
         return t.render(Context(dictionary))
165 166
     # 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):
167 168
     with context_instance.push(dictionary):
168 169
         return t.render(context_instance)
169 170
 
170  
-def select_template(template_name_list):
  171
+def select_template(template_name_list, dirs=None):
171 172
     "Given a list of template names, returns the first that can be loaded."
172 173
     if not template_name_list:
173 174
         raise TemplateDoesNotExist("No template names provided")
174 175
     not_found = []
175 176
     for template_name in template_name_list:
176 177
         try:
177  
-            return get_template(template_name)
  178
+            return get_template(template_name, dirs)
178 179
         except TemplateDoesNotExist as e:
179 180
             if e.args[0] not in not_found:
180 181
                 not_found.append(e.args[0])
18  docs/ref/templates/api.txt
@@ -594,17 +594,31 @@ The Python API
594 594
 
595 595
 ``django.template.loader`` has two functions to load templates from files:
596 596
 
597  
-.. function:: get_template(template_name)
  597
+.. function:: get_template(template_name[, dirs])
598 598
 
599 599
     ``get_template`` returns the compiled template (a ``Template`` object) for
600 600
     the template with the given name. If the template doesn't exist, it raises
601 601
     ``django.template.TemplateDoesNotExist``.
602 602
 
603  
-.. function:: select_template(template_name_list)
  603
+    To override the :setting:`TEMPLATE_DIRS` setting, use the ``dirs``
  604
+    parameter. The ``dirs`` parameter may be a tuple or list.
  605
+
  606
+    .. versionchanged:: 1.7
  607
+
  608
+       The ``dirs`` parameter was added.
  609
+
  610
+.. function:: select_template(template_name_list[, dirs])
604 611
 
605 612
     ``select_template`` is just like ``get_template``, except it takes a list
606 613
     of template names. Of the list, it returns the first template that exists.
607 614
 
  615
+    To override the :setting:`TEMPLATE_DIRS` setting, use the ``dirs``
  616
+    parameter. The ``dirs`` parameter may be a tuple or list.
  617
+
  618
+    .. versionchanged:: 1.7
  619
+
  620
+       The ``dirs`` parameter was added.
  621
+
608 622
 For example, if you call ``get_template('story_detail.html')`` and have the
609 623
 above :setting:`TEMPLATE_DIRS` setting, here are the files Django will look for,
610 624
 in order:
8  docs/releases/1.7.txt
@@ -285,6 +285,14 @@ Templates
285 285
 * ``TypeError`` exceptions are not longer silenced when raised during the
286 286
   rendering of a template.
287 287
 
  288
+* The following functions now accept a ``dirs`` parameter which is a list or
  289
+  tuple to override :setting:`TEMPLATE_DIRS`:
  290
+
  291
+  * :func:`django.template.loader.get_template()`
  292
+  * :func:`django.template.loader.select_template()`
  293
+  * :func:`django.shortcuts.render()`
  294
+  * :func:`django.shortcuts.render_to_response()`
  295
+
288 296
 Tests
289 297
 ^^^^^
290 298
 
35  docs/topics/http/shortcuts.txt
@@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake.
15 15
 ``render``
16 16
 ==========
17 17
 
18  
-.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app])
  18
+.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app][, dirs])
19 19
 
20 20
    Combines a given template with a given context dictionary and returns an
21 21
    :class:`~django.http.HttpResponse` object with that rendered text.
@@ -58,6 +58,13 @@ Optional arguments
58 58
     :ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`
59 59
     for more information.
60 60
 
  61
+``dirs``
  62
+    A tuple or list of values to override the :setting:`TEMPLATE_DIRS` setting.
  63
+
  64
+.. versionchanged:: 1.7
  65
+
  66
+   The ``dirs`` parameter was added.
  67
+
61 68
 Example
62 69
 -------
63 70
 
@@ -83,11 +90,19 @@ This example is equivalent to::
83 90
         return HttpResponse(t.render(c),
84 91
             content_type="application/xhtml+xml")
85 92
 
  93
+If you want to override the :setting:`TEMPLATE_DIRS` setting, use the
  94
+``dirs`` parameter::
  95
+
  96
+    from django.shortcuts import render
  97
+
  98
+    def my_view(request):
  99
+        # View code here...
  100
+        return render(request, 'index.html', dirs=('custom_templates',))
86 101
 
87 102
 ``render_to_response``
88 103
 ======================
89 104
 
90  
-.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type])
  105
+.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type][, dirs])
91 106
 
92 107
    Renders a given template with a given context dictionary and returns an
93 108
    :class:`~django.http.HttpResponse` object with that rendered text.
@@ -125,6 +140,13 @@ Optional arguments
125 140
     The MIME type to use for the resulting document. Defaults to the value of
126 141
     the :setting:`DEFAULT_CONTENT_TYPE` setting.
127 142
 
  143
+``dirs``
  144
+    A tuple or list of values to override the :setting:`TEMPLATE_DIRS` setting.
  145
+
  146
+.. versionchanged:: 1.7
  147
+
  148
+   The ``dirs`` parameter was added.
  149
+
128 150
 Example
129 151
 -------
130 152
 
@@ -150,6 +172,15 @@ This example is equivalent to::
150 172
         return HttpResponse(t.render(c),
151 173
             content_type="application/xhtml+xml")
152 174
 
  175
+If you want to override the :setting:`TEMPLATE_DIRS` setting, use the
  176
+``dirs`` parameter::
  177
+
  178
+    from django.shortcuts import render_to_response
  179
+
  180
+    def my_view(request):
  181
+        # View code here...
  182
+        return render_to_response('index.html', dirs=('custom_templates',))
  183
+
153 184
 ``redirect``
154 185
 ============
155 186
 
1  tests/template_tests/other_templates/test_dirs.html
... ...
@@ -0,0 +1 @@
  1
+spam eggs{{ obj }}
22  tests/template_tests/test_loaders.py
@@ -174,8 +174,28 @@ def test_empty_list(self):
174 174
                                 'No template names provided$',
175 175
                                 loader.render_to_string, [])
176 176
 
177  
-
178 177
     def test_select_templates_from_empty_list(self):
179 178
         six.assertRaisesRegex(self, TemplateDoesNotExist,
180 179
                                 'No template names provided$',
181 180
                                 loader.select_template, [])
  181
+
  182
+
  183
+class TemplateDirsOverrideTest(unittest.TestCase):
  184
+
  185
+    dirs_tuple = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
  186
+    dirs_list = list(dirs_tuple)
  187
+    dirs_iter = (dirs_tuple, dirs_list)
  188
+
  189
+    def test_render_to_string(self):
  190
+        for dirs in self.dirs_iter:
  191
+            self.assertEqual(loader.render_to_string('test_dirs.html', dirs=dirs), 'spam eggs\n')
  192
+
  193
+    def test_get_template(self):
  194
+        for dirs in self.dirs_iter:
  195
+            template = loader.get_template('test_dirs.html', dirs=dirs)
  196
+            self.assertEqual(template.render(Context({})), 'spam eggs\n')
  197
+
  198
+    def test_select_template(self):
  199
+        for dirs in self.dirs_iter:
  200
+            template = loader.select_template(['test_dirs.html'], dirs=dirs)
  201
+            self.assertEqual(template.render(Context({})), 'spam eggs\n')
2  tests/view_tests/generic_urls.py
@@ -48,10 +48,12 @@
48 48
     (r'^shortcuts/render_to_response/$', 'render_to_response_view'),
49 49
     (r'^shortcuts/render_to_response/request_context/$', 'render_to_response_view_with_request_context'),
50 50
     (r'^shortcuts/render_to_response/content_type/$', 'render_to_response_view_with_content_type'),
  51
+    (r'^shortcuts/render_to_response/dirs/$', 'render_to_response_view_with_dirs'),
51 52
     (r'^shortcuts/render/$', 'render_view'),
52 53
     (r'^shortcuts/render/base_context/$', 'render_view_with_base_context'),
53 54
     (r'^shortcuts/render/content_type/$', 'render_view_with_content_type'),
54 55
     (r'^shortcuts/render/status/$', 'render_view_with_status'),
55 56
     (r'^shortcuts/render/current_app/$', 'render_view_with_current_app'),
  57
+    (r'^shortcuts/render/dirs/$', 'render_with_dirs'),
56 58
     (r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
57 59
 )
1  tests/view_tests/other_templates/render_dirs_test.html
... ...
@@ -0,0 +1 @@
  1
+spam eggs
12  tests/view_tests/tests/test_shortcuts.py
@@ -27,6 +27,12 @@ def test_render_to_response_with_content_type(self):
27 27
         self.assertEqual(response.content, b'FOO.BAR..\n')
28 28
         self.assertEqual(response['Content-Type'], 'application/x-rendertest')
29 29
 
  30
+    def test_render_to_response_with_dirs(self):
  31
+        response = self.client.get('/shortcuts/render_to_response/dirs/')
  32
+        self.assertEqual(response.status_code, 200)
  33
+        self.assertEqual(response.content, b'spam eggs\n')
  34
+        self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
  35
+
30 36
     def test_render(self):
31 37
         response = self.client.get('/shortcuts/render/')
32 38
         self.assertEqual(response.status_code, 200)
@@ -55,5 +61,11 @@ def test_render_with_current_app(self):
55 61
         response = self.client.get('/shortcuts/render/current_app/')
56 62
         self.assertEqual(response.context.current_app, "foobar_app")
57 63
 
  64
+    def test_render_with_dirs(self):
  65
+        response = self.client.get('/shortcuts/render/dirs/')
  66
+        self.assertEqual(response.status_code, 200)
  67
+        self.assertEqual(response.content, b'spam eggs\n')
  68
+        self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
  69
+
58 70
     def test_render_with_current_app_conflict(self):
59 71
         self.assertRaises(ValueError, self.client.get, '/shortcuts/render/current_app_conflict/')
9  tests/view_tests/views.py
... ...
@@ -1,5 +1,6 @@
1 1
 from __future__ import unicode_literals
2 2
 
  3
+import os
3 4
 import sys
4 5
 
5 6
 from django.core.exceptions import PermissionDenied, SuspiciousOperation
@@ -10,10 +11,12 @@
10 11
 from django.views.debug import technical_500_response, SafeExceptionReporterFilter
11 12
 from django.views.decorators.debug import (sensitive_post_parameters,
12 13
                                            sensitive_variables)
  14
+from django.utils._os import upath
13 15
 from django.utils.log import getLogger
14 16
 
15 17
 from . import BrokenException, except_args
16 18
 
  19
+dirs = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
17 20
 
18 21
 
19 22
 def index_page(request):
@@ -85,6 +88,9 @@ def render_to_response_view_with_content_type(request):
85 88
         'bar': 'BAR',
86 89
     }, content_type='application/x-rendertest')
87 90
 
  91
+def render_to_response_view_with_dirs(request):
  92
+    return render_to_response('render_dirs_test.html', dirs=dirs)
  93
+
88 94
 def render_view(request):
89 95
     return render(request, 'debug/render_test.html', {
90 96
         'foo': 'FOO',
@@ -123,6 +129,9 @@ def render_view_with_current_app_conflict(request):
123 129
         'bar': 'BAR',
124 130
     }, current_app="foobar_app", context_instance=RequestContext(request))
125 131
 
  132
+def render_with_dirs(request):
  133
+    return render(request, 'render_dirs_test.html', dirs=dirs)
  134
+
126 135
 def raises_template_does_not_exist(request, path='i_dont_exist.html'):
127 136
     # We need to inspect the HTML generated by the fancy 500 debug view but
128 137
     # the test client ignores it, so we send it explicitly.

0 notes on commit 2f0566f

Please sign in to comment.
Something went wrong with that request. Please try again.