diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 318ae5f..b595a54 100644
a
|
b
|
class WidthRatioNode(Node):
|
410 | 410 | return str(int(round(ratio))) |
411 | 411 | |
412 | 412 | class WithNode(Node): |
413 | | def __init__(self, var, name, nodelist): |
414 | | self.var = var |
415 | | self.name = name |
| 413 | def __init__(self, names, vars, nodelist): |
| 414 | self.names = names |
| 415 | self.vars = vars |
416 | 416 | self.nodelist = nodelist |
417 | 417 | |
418 | 418 | def __repr__(self): |
419 | 419 | return "<WithNode>" |
420 | 420 | |
421 | 421 | def render(self, context): |
422 | | val = self.var.resolve(context) |
| 422 | values = [var.resolve(context) for var in self.vars] |
423 | 423 | context.push() |
424 | | context[self.name] = val |
| 424 | for name, value in zip(self.names, values): |
| 425 | context[name] = value |
425 | 426 | output = self.nodelist.render(context) |
426 | 427 | context.pop() |
427 | 428 | return output |
… |
… |
widthratio = register.tag(widthratio)
|
1191 | 1192 | def do_with(parser, token): |
1192 | 1193 | """ |
1193 | 1194 | Adds a value to the context (inside of this block) for caching and easy |
1194 | | access. |
| 1195 | access. You bind a value to a new name using ``as``. You can chain multiple |
| 1196 | bindings with ``and``. This works simillar to Python assignment. |
1195 | 1197 | |
1196 | 1198 | For example:: |
1197 | 1199 | |
1198 | 1200 | {% with person.some_sql_method as total %} |
1199 | 1201 | {{ total }} object{{ total|pluralize }} |
1200 | 1202 | {% endwith %} |
| 1203 | |
| 1204 | {% with 0 as z and x|add:5 as y and y as x %} |
| 1205 | ({{ x }}, {{ y }}, {{ z }}) |
| 1206 | {% endwith %} |
1201 | 1207 | """ |
1202 | 1208 | bits = list(token.split_contents()) |
1203 | | if len(bits) != 4 or bits[2] != "as": |
1204 | | raise TemplateSyntaxError("%r expected format is 'value as name'" % |
1205 | | bits[0]) |
1206 | | var = parser.compile_filter(bits[1]) |
1207 | | name = bits[3] |
| 1209 | try: |
| 1210 | if len(bits) % 4: |
| 1211 | raise TemplateSyntaxError() |
| 1212 | for b in bits[2::4]: |
| 1213 | if b != 'as': |
| 1214 | raise TemplateSyntaxError() |
| 1215 | for b in bits[4::4]: |
| 1216 | if b != 'and': |
| 1217 | raise TemplateSyntaxError() |
| 1218 | except TemplateSyntaxError: |
| 1219 | raise TemplateSyntaxError("'%s' statements should use the format " |
| 1220 | "'with value as name [and value as name]...'" |
| 1221 | ": %s" % (bits[0], token.contents)) |
| 1222 | varlist = [parser.compile_filter(b) for b in bits[1::4]] |
| 1223 | namelist = bits[3::4] |
1208 | 1224 | nodelist = parser.parse(('endwith',)) |
1209 | 1225 | parser.delete_first_token() |
1210 | | return WithNode(var, name, nodelist) |
| 1226 | return WithNode(namelist, varlist, nodelist) |
1211 | 1227 | do_with = register.tag('with', do_with) |
diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index b8bc741..8a4edd9 100644
a
|
b
|
|
1 | 1 | from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable |
2 | 2 | from django.template import Library, Node, TextNode |
3 | 3 | from django.template.loader import get_template |
| 4 | from django.template.defaulttags import WithNode, kwarg_re |
4 | 5 | from django.conf import settings |
5 | 6 | from django.utils.safestring import mark_safe |
6 | 7 | |
… |
… |
def do_extends(parser, token):
|
201 | 202 | |
202 | 203 | def do_include(parser, token): |
203 | 204 | """ |
204 | | Loads a template and renders it with the current context. |
| 205 | Loads a template and renders it with the current context. You can pass |
| 206 | additional context using keyword arguments. |
205 | 207 | |
206 | 208 | Example:: |
207 | 209 | |
208 | 210 | {% include "foo/some_include" %} |
| 211 | {% include "foo/some_include" with bar="BAZZ!" %} |
| 212 | |
209 | 213 | """ |
210 | 214 | bits = token.split_contents() |
211 | | if len(bits) != 2: |
212 | | raise TemplateSyntaxError("%r tag takes one argument: the name of the template to be included" % bits[0]) |
| 215 | if len(bits) < 2: |
| 216 | raise TemplateSyntaxError("%r tag takes at least one argument: the name of the template to be included." % bits[0]) |
| 217 | namelist, varlist = [], [] |
| 218 | if len(bits) > 2: |
| 219 | if bits[2] == 'with': |
| 220 | for bit in bits[3:]: |
| 221 | match = kwarg_re.match(bit) |
| 222 | if not match: |
| 223 | raise TemplateSyntaxError("Malformed arguments to %r tag." % bits[0]) |
| 224 | name, value = match.groups() |
| 225 | if not name: |
| 226 | raise TemplateSyntaxError('''"with" part of %r tag accepts only keyword arguments.''' % bits[0]) |
| 227 | namelist.append(name) |
| 228 | varlist.append(parser.compile_filter(value)) |
| 229 | if not namelist: |
| 230 | raise TemplateSyntaxError('''"with" in %r tag needs at least one keyword argument.''' % bits[0]) |
| 231 | else: |
| 232 | raise TemplateSyntaxError('''Invalid option %r for %r tag.''' % (bits[2], bits[0])) |
213 | 233 | path = bits[1] |
214 | 234 | if path[0] in ('"', "'") and path[-1] == path[0]: |
215 | | return ConstantIncludeNode(path[1:-1]) |
216 | | return IncludeNode(bits[1]) |
| 235 | node = ConstantIncludeNode(path[1:-1]) |
| 236 | else: |
| 237 | node = IncludeNode(bits[1]) |
| 238 | if namelist: |
| 239 | nodelist = parser.create_nodelist() |
| 240 | parser.extend_nodelist(nodelist, node, token) |
| 241 | node = WithNode(namelist, varlist, nodelist) |
| 242 | return node |
217 | 243 | |
218 | 244 | register.tag('block', do_block) |
219 | 245 | register.tag('extends', do_extends) |
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 002aa3f..7a08994 100644
a
|
b
|
For example::
|
1036 | 1036 | The populated variable (in the example above, ``total``) is only available |
1037 | 1037 | between the ``{% with %}`` and ``{% endwith %}`` tags. |
1038 | 1038 | |
| 1039 | Multiple variables can be cached by using the word ``"and"`` as a delimiter:: |
| 1040 | |
| 1041 | {% with items.0 as first and items|slice:"1:" as rest %} |
| 1042 | {{ first }} comes first and {{ rest|join:", " }} follow. |
| 1043 | {% endwith %} |
| 1044 | |
1039 | 1045 | .. _ref-templates-builtins-filters: |
1040 | 1046 | |
1041 | 1047 | Built-in filter reference |
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 5902e8d..1290c86 100644
a
|
b
|
class Templates(unittest.TestCase):
|
911 | 911 | 'include 05': ('template with a space', {}, 'template with a space'), |
912 | 912 | 'include06': ('{% include "include 05"%}', {}, 'template with a space'), |
913 | 913 | |
| 914 | # extra inline context |
| 915 | 'include07': ('{% include "basic-syntax02" with headline="Inline" %}', {'headline': 'Included'}, 'Inline'), |
| 916 | 'include08': ('{{ first }}--{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}--{{ second }}', {'first': 'Ul', 'second': 'lU'}, 'Ul--LU --- UL--lU'), |
| 917 | |
| 918 | 'include-error01': ('{% include "basic-syntax01" with %}', {}, template.TemplateSyntaxError), |
| 919 | 'include-error02': ('{% include "basic-syntax01" with "no key" %}', {}, template.TemplateSyntaxError), |
| 920 | 'include-error03': ('{% include "basic-syntax01" with dotted.arg="error" %}', {}, template.TemplateSyntaxError), |
| 921 | 'include-error04': ('{% include "basic-syntax01" something_random %}', {}, template.TemplateSyntaxError), |
| 922 | |
914 | 923 | ### NAMED ENDBLOCKS ####################################################### |
915 | 924 | |
916 | 925 | # Basic test |
… |
… |
class Templates(unittest.TestCase):
|
1197 | 1206 | ### WITH TAG ######################################################## |
1198 | 1207 | 'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'), |
1199 | 1208 | 'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')), |
| 1209 | 'with03': ('{% with dict.k1 as k1 and dict.k2 as k2 %}{{ k1 }}+{{ k2 }}{% endwith %}', {'dict': {'k1':3, 'k2':4}}, '3+4'), |
| 1210 | 'with04': ('{% with 42 as z and x|add:5 as y and y as x %}({{ x }},{{ y }},{{ z }}){% endwith %}', {'x': 13, 'y': -5}, '(-5,18,42)'), |
1200 | 1211 | |
1201 | 1212 | 'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError), |
1202 | 1213 | 'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError), |
| 1214 | 'with-error03': ('{% with dict.k1 as k1 and dict.k2 %}{{ key }}{% endwith %}', {'dict': {'k1':3, 'k2':4}}, template.TemplateSyntaxError), |
| 1215 | 'with-error04': ('{% with dict.k1 equals k1 %}{{ key }}{% endwith %}', {'dict': {'k1':3, 'k2':4}}, template.TemplateSyntaxError), |
| 1216 | 'with-error05': ('{% with dict.k1 as k1, dict.k2 as k2 %}{{ key }}{% endwith %}', {'dict': {'k1':3, 'k2':4}}, template.TemplateSyntaxError), |
1203 | 1217 | |
1204 | 1218 | ### NOW TAG ######################################################## |
1205 | 1219 | # Simple case |