Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixes #7817 and #9456.

- The include tag now has a 'with' option to include to provide extra context
  vairables to the included template.

- The include tag now has an 'only' option to exclude the current context
  when rendering the included template.

- The with tag now accepts multiple variable assignments.

- The with, include and blocktrans tags now use a new keyword argument format
  for variable assignments (e.g. `{% with foo=1 bar=2 %}`).

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14922 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3ae9117c467f9fabed8736949dee209d40293b8d 1 parent 99742d8
Chris Beaven authored December 18, 2010
102  django/template/defaulttags.py
@@ -16,6 +16,55 @@
16 16
 # Regex for token keyword arguments
17 17
 kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")
18 18
 
  19
+def token_kwargs(bits, parser, support_legacy=False):
  20
+    """
  21
+    A utility method for parsing token keyword arguments.
  22
+
  23
+    :param bits: A list containing remainder of the token (split by spaces)
  24
+        that is to be checked for arguments. Valid arguments will be removed
  25
+        from this list.
  26
+
  27
+    :param support_legacy: If set to true ``True``, the legacy format
  28
+        ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1``
  29
+        format is allowed.
  30
+
  31
+    :returns: A dictionary of the arguments retrieved from the ``bits`` token
  32
+        list.
  33
+
  34
+    There is no requirement for all remaining token ``bits`` to be keyword
  35
+    arguments, so the dictionary will be returned as soon as an invalid
  36
+    argument format is reached.
  37
+    """
  38
+    if not bits:
  39
+        return {}
  40
+    match = kwarg_re.match(bits[0])
  41
+    kwarg_format = match and match.group(1)
  42
+    if not kwarg_format:
  43
+        if not support_legacy:
  44
+            return {}
  45
+        if len(bits) < 3 or bits[1] != 'as':
  46
+            return {}
  47
+
  48
+    kwargs = {}
  49
+    while bits:
  50
+        if kwarg_format: 
  51
+            match = kwarg_re.match(bits[0])
  52
+            if not match or not match.group(1):
  53
+                return kwargs
  54
+            key, value = match.groups()
  55
+            del bits[:1]
  56
+        else:
  57
+            if len(bits) < 3 or bits[1] != 'as':
  58
+                return kwargs
  59
+            key, value = bits[2], bits[0]
  60
+            del bits[:3]
  61
+        kwargs[key] = parser.compile_filter(value)
  62
+        if bits and not kwarg_format:
  63
+            if bits[0] != 'and':
  64
+                return kwargs
  65
+            del bits[:1]
  66
+    return kwargs
  67
+
19 68
 class AutoEscapeControlNode(Node):
20 69
     """Implements the actions of the autoescape tag."""
21 70
     def __init__(self, setting, nodelist):
@@ -298,7 +347,7 @@ def __init__(self, filepath, parsed, legacy_filepath=True):
298 347
     def render(self, context):
299 348
         filepath = self.filepath
300 349
         if not self.legacy_filepath:
301  
-           filepath = filepath.resolve(context)
  350
+            filepath = filepath.resolve(context)
302 351
 
303 352
         if not include_is_allowed(filepath):
304 353
             if settings.DEBUG:
@@ -433,18 +482,25 @@ def render(self, context):
433 482
         return str(int(round(ratio)))
434 483
 
435 484
 class WithNode(Node):
436  
-    def __init__(self, var, name, nodelist):
437  
-        self.var = var
438  
-        self.name = name
  485
+    def __init__(self, var, name, nodelist, extra_context=None,
  486
+                 isolated_context=False):
439 487
         self.nodelist = nodelist
  488
+        # var and name are legacy attributes, being left in case they are used
  489
+        # by third-party subclasses of this Node.
  490
+        self.extra_context = extra_context or {}
  491
+        if name:
  492
+            self.extra_context[name] = var
  493
+        self.isolated_context = isolated_context
440 494
 
441 495
     def __repr__(self):
442 496
         return "<WithNode>"
443 497
 
444 498
     def render(self, context):
445  
-        val = self.var.resolve(context)
446  
-        context.push()
447  
-        context[self.name] = val
  499
+        values = dict([(key, val.resolve(context)) for key, val in
  500
+                       self.extra_context.iteritems()])
  501
+        if self.isolated_context:
  502
+            return self.nodelist.render(Context(values))
  503
+        context.update(values)
448 504
         output = self.nodelist.render(context)
449 505
         context.pop()
450 506
         return output
@@ -1276,22 +1332,34 @@ def widthratio(parser, token):
1276 1332
 #@register.tag
1277 1333
 def do_with(parser, token):
1278 1334
     """
1279  
-    Adds a value to the context (inside of this block) for caching and easy
1280  
-    access.
  1335
+    Adds one or more values to the context (inside of this block) for caching
  1336
+    and easy access.
1281 1337
 
1282 1338
     For example::
1283 1339
 
1284  
-        {% with person.some_sql_method as total %}
  1340
+        {% with total=person.some_sql_method %}
1285 1341
             {{ total }} object{{ total|pluralize }}
1286 1342
         {% endwith %}
  1343
+
  1344
+    Multiple values can be added to the context::
  1345
+
  1346
+        {% with foo=1 bar=2 %}
  1347
+            ...
  1348
+        {% endwith %}
  1349
+
  1350
+    The legacy format of ``{% with person.some_sql_method as total %}`` is
  1351
+    still accepted.
1287 1352
     """
1288  
-    bits = list(token.split_contents())
1289  
-    if len(bits) != 4 or bits[2] != "as":
1290  
-        raise TemplateSyntaxError("%r expected format is 'value as name'" %
1291  
-                                  bits[0])
1292  
-    var = parser.compile_filter(bits[1])
1293  
-    name = bits[3]
  1353
+    bits = token.split_contents()
  1354
+    remaining_bits = bits[1:]
  1355
+    extra_context = token_kwargs(remaining_bits, parser, support_legacy=True)
  1356
+    if not extra_context:
  1357
+        raise TemplateSyntaxError("%r expected at least one variable "
  1358
+                                  "assignment" % bits[0])
  1359
+    if remaining_bits:
  1360
+        raise TemplateSyntaxError("%r received an invalid token: %r" %
  1361
+                                  (bits[0], remaining_bits[0]))
1294 1362
     nodelist = parser.parse(('endwith',))
1295 1363
     parser.delete_first_token()
1296  
-    return WithNode(var, name, nodelist)
  1364
+    return WithNode(None, None, nodelist, extra_context=extra_context)
1297 1365
 do_with = register.tag('with', do_with)
81  django/template/loader_tags.py
... ...
@@ -1,5 +1,7 @@
1 1
 from django.template.base import TemplateSyntaxError, TemplateDoesNotExist, Variable
2 2
 from django.template.base import Library, Node, TextNode
  3
+from django.template.context import Context
  4
+from django.template.defaulttags import token_kwargs
3 5
 from django.template.loader import get_template
4 6
 from django.conf import settings
5 7
 from django.utils.safestring import mark_safe
@@ -124,8 +126,25 @@ def render(self, context):
124 126
         # the same.
125 127
         return compiled_parent._render(context)
126 128
 
127  
-class ConstantIncludeNode(Node):
128  
-    def __init__(self, template_path):
  129
+class BaseIncludeNode(Node):
  130
+    def __init__(self, *args, **kwargs):
  131
+        self.extra_context = kwargs.pop('extra_context', {})
  132
+        self.isolated_context = kwargs.pop('isolated_context', False)
  133
+        super(BaseIncludeNode, self).__init__(*args, **kwargs)
  134
+
  135
+    def render_template(self, template, context):
  136
+        values = dict([(name, var.resolve(context)) for name, var
  137
+                       in self.extra_context.iteritems()])
  138
+        if self.isolated_context:
  139
+            return template.render(Context(values))
  140
+        context.update(values)
  141
+        output = template.render(context)
  142
+        context.pop()
  143
+        return output
  144
+
  145
+class ConstantIncludeNode(BaseIncludeNode):
  146
+    def __init__(self, template_path, *args, **kwargs):
  147
+        super(ConstantIncludeNode, self).__init__(*args, **kwargs)
129 148
         try:
130 149
             t = get_template(template_path)
131 150
             self.template = t
@@ -135,21 +154,21 @@ def __init__(self, template_path):
135 154
             self.template = None
136 155
 
137 156
     def render(self, context):
138  
-        if self.template:
139  
-            return self.template.render(context)
140  
-        else:
  157
+        if not self.template:
141 158
             return ''
  159
+        return self.render_template(self.template, context)
142 160
 
143  
-class IncludeNode(Node):
144  
-    def __init__(self, template_name):
145  
-        self.template_name = Variable(template_name)
  161
+class IncludeNode(BaseIncludeNode):
  162
+    def __init__(self, template_name, *args, **kwargs):
  163
+        super(IncludeNode, self).__init__(*args, **kwargs)
  164
+        self.template_name = template_name
146 165
 
147 166
     def render(self, context):
148 167
         try:
149 168
             template_name = self.template_name.resolve(context)
150  
-            t = get_template(template_name)
151  
-            return t.render(context)
152  
-        except TemplateSyntaxError, e:
  169
+            template = get_template(template_name)
  170
+            return self.render_template(template, context)
  171
+        except TemplateSyntaxError:
153 172
             if settings.TEMPLATE_DEBUG:
154 173
                 raise
155 174
             return ''
@@ -201,19 +220,49 @@ def do_extends(parser, token):
201 220
 
202 221
 def do_include(parser, token):
203 222
     """
204  
-    Loads a template and renders it with the current context.
  223
+    Loads a template and renders it with the current context. You can pass
  224
+    additional context using keyword arguments.
205 225
 
206 226
     Example::
207 227
 
208 228
         {% include "foo/some_include" %}
  229
+        {% include "foo/some_include" with bar="BAZZ!" baz="BING!" %}
  230
+
  231
+    Use the ``only`` argument to exclude the current context when rendering
  232
+    the included template::
  233
+
  234
+        {% include "foo/some_include" only %}
  235
+        {% include "foo/some_include" with bar="1" only %}
209 236
     """
210 237
     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])
  238
+    if len(bits) < 2:
  239
+        raise TemplateSyntaxError("%r tag takes at least one argument: the name of the template to be included." % bits[0])
  240
+    options = {}
  241
+    remaining_bits = bits[2:]
  242
+    while remaining_bits:
  243
+        option = remaining_bits.pop(0)
  244
+        if option in options:
  245
+            raise TemplateSyntaxError('The %r option was specified more '
  246
+                                      'than once.' % option)
  247
+        if option == 'with':
  248
+            value = token_kwargs(remaining_bits, parser, support_legacy=False)
  249
+            if not value:
  250
+                raise TemplateSyntaxError('"with" in %r tag needs at least '
  251
+                                          'one keyword argument.' % bits[0])
  252
+        elif option == 'only':
  253
+            value = True
  254
+        else:
  255
+            raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
  256
+                                      (bits[0], option))
  257
+        options[option] = value
  258
+    isolated_context = options.get('only', False)
  259
+    namemap = options.get('with', {})
213 260
     path = bits[1]
214 261
     if path[0] in ('"', "'") and path[-1] == path[0]:
215  
-        return ConstantIncludeNode(path[1:-1])
216  
-    return IncludeNode(bits[1])
  262
+        return ConstantIncludeNode(path[1:-1], extra_context=namemap,
  263
+                                   isolated_context=isolated_context)
  264
+    return IncludeNode(parser.compile_filter(bits[1]), extra_context=namemap,
  265
+                       isolated_context=isolated_context)
217 266
 
218 267
 register.tag('block', do_block)
219 268
 register.tag('extends', do_extends)
64  django/templatetags/i18n.py
@@ -6,6 +6,7 @@
6 6
 from django.template.base import _render_value_in_context
7 7
 from django.utils import translation
8 8
 from django.utils.encoding import force_unicode
  9
+from django.template.defaulttags import token_kwargs
9 10
 
10 11
 register = Library()
11 12
 
@@ -97,7 +98,7 @@ def render_token_list(self, tokens):
97 98
     def render(self, context):
98 99
         tmp_context = {}
99 100
         for var, val in self.extra_context.items():
100  
-            tmp_context[var] = val.render(context)
  101
+            tmp_context[var] = val.resolve(context)
101 102
         # Update() works like a push(), so corresponding context.pop() is at
102 103
         # the end of function
103 104
         context.update(tmp_context)
@@ -284,43 +285,54 @@ def do_block_translate(parser, token):
284 285
 
285 286
     Usage::
286 287
 
287  
-        {% blocktrans with foo|filter as bar and baz|filter as boo %}
  288
+        {% blocktrans with bar=foo|filter boo=baz|filter %}
288 289
         This is {{ bar }} and {{ boo }}.
289 290
         {% endblocktrans %}
290 291
 
291 292
     Additionally, this supports pluralization::
292 293
 
293  
-        {% blocktrans count var|length as count %}
  294
+        {% blocktrans count count=var|length %}
294 295
         There is {{ count }} object.
295 296
         {% plural %}
296 297
         There are {{ count }} objects.
297 298
         {% endblocktrans %}
298 299
 
299 300
     This is much like ngettext, only in template syntax.
300  
-    """
301  
-    class BlockTranslateParser(TokenParser):
302  
-        def top(self):
303  
-            countervar = None
304  
-            counter = None
305  
-            extra_context = {}
306  
-            while self.more():
307  
-                tag = self.tag()
308  
-                if tag == 'with' or tag == 'and':
309  
-                    value = self.value()
310  
-                    if self.tag() != 'as':
311  
-                        raise TemplateSyntaxError("variable bindings in 'blocktrans' must be 'with value as variable'")
312  
-                    extra_context[self.tag()] = VariableNode(
313  
-                            parser.compile_filter(value))
314  
-                elif tag == 'count':
315  
-                    counter = parser.compile_filter(self.value())
316  
-                    if self.tag() != 'as':
317  
-                        raise TemplateSyntaxError("counter specification in 'blocktrans' must be 'count value as variable'")
318  
-                    countervar = self.tag()
319  
-                else:
320  
-                    raise TemplateSyntaxError("unknown subtag %s for 'blocktrans' found" % tag)
321  
-            return (countervar, counter, extra_context)
322 301
 
323  
-    countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
  302
+    The "var as value" legacy format is still supported::
  303
+
  304
+        {% blocktrans with foo|filter as bar and baz|filter as boo %}
  305
+        {% blocktrans count var|length as count %}
  306
+    """
  307
+    bits = token.split_contents()
  308
+
  309
+    options = {}
  310
+    remaining_bits = bits[1:]
  311
+    while remaining_bits:
  312
+        option = remaining_bits.pop(0)
  313
+        if option in options:
  314
+            raise TemplateSyntaxError('The %r option was specified more '
  315
+                                      'than once.' % option)
  316
+        if option == 'with':
  317
+            value = token_kwargs(remaining_bits, parser, support_legacy=True)
  318
+            if not value:
  319
+                raise TemplateSyntaxError('"with" in %r tag needs at least '
  320
+                                          'one keyword argument.' % bits[0])
  321
+        elif option == 'count':
  322
+            value = token_kwargs(remaining_bits, parser, support_legacy=True)
  323
+            if len(value) != 1:
  324
+                raise TemplateSyntaxError('"count" in %r tag expected exactly '
  325
+                                          'one keyword argument.' % bits[0])
  326
+        else:
  327
+            raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
  328
+                                      (bits[0], option))
  329
+        options[option] = value
  330
+
  331
+    if 'count' in options:
  332
+        countervar, counter = options['count'].items()[0]
  333
+    else:
  334
+        countervar, counter = None, None
  335
+    extra_context = options.get('with', {}) 
324 336
 
325 337
     singular = []
326 338
     plural = []
30  docs/ref/templates/builtins.txt
@@ -639,9 +639,19 @@ including it. This example produces the output ``"Hello, John"``:
639 639
 
640 640
     * The ``name_snippet.html`` template::
641 641
 
642  
-        Hello, {{ person }}
  642
+        {{ greeting }}, {{ person|default:"friend" }}!
643 643
 
644  
-See also: ``{% ssi %}``.
  644
+.. versionchanged:: 1.3
  645
+   Additional context and exclusive context.
  646
+
  647
+You can pass additional context to the template using keyword arguments::
  648
+
  649
+    {% include "name_snippet.html" with person="Jane" greeting="Hello" "%}
  650
+
  651
+If you want to only render the context with the variables provided (or even
  652
+no variables at all), use the ``only`` option::
  653
+
  654
+    {% include "name_snippet.html" with greeting="Hi" only %}
645 655
 
646 656
 .. note::
647 657
     The :ttag:`include` tag should be considered as an implementation of
@@ -650,6 +660,8 @@ See also: ``{% ssi %}``.
650 660
     This means that there is no shared state between included templates --
651 661
     each include is a completely independent rendering process.
652 662
 
  663
+See also: ``{% ssi %}``.
  664
+
653 665
 .. templatetag:: load
654 666
 
655 667
 load
@@ -1044,18 +1056,30 @@ with
1044 1056
 
1045 1057
 .. versionadded:: 1.0
1046 1058
 
  1059
+.. versionchanged:: 1.3
  1060
+   New keyword argument format and multiple variable assignments.
  1061
+
1047 1062
 Caches a complex variable under a simpler name. This is useful when accessing
1048 1063
 an "expensive" method (e.g., one that hits the database) multiple times.
1049 1064
 
1050 1065
 For example::
1051 1066
 
1052  
-    {% with business.employees.count as total %}
  1067
+    {% with total=business.employees.count %}
1053 1068
         {{ total }} employee{{ total|pluralize }}
1054 1069
     {% endwith %}
1055 1070
 
1056 1071
 The populated variable (in the example above, ``total``) is only available
1057 1072
 between the ``{% with %}`` and ``{% endwith %}`` tags.
1058 1073
 
  1074
+You can assign more than one context variable::
  1075
+
  1076
+    {% with alpha=1 beta=2 %}
  1077
+        ...
  1078
+    {% endwith %}
  1079
+
  1080
+.. note:: The previous more verbose format is still supported:
  1081
+   ``{% with business.employees.count as total %}``
  1082
+
1059 1083
 .. _ref-templates-builtins-filters:
1060 1084
 
1061 1085
 Built-in filter reference
2  docs/topics/db/optimization.txt
@@ -206,7 +206,7 @@ many-to-many relation to User, the following template code is optimal:
206 206
 .. code-block:: html+django
207 207
 
208 208
    {% if display_inbox %}
209  
-     {% with user.emails.all as emails %}
  209
+     {% with emails=user.emails.all %}
210 210
        {% if emails %}
211 211
          <p>You have {{ emails|length }} email(s)</p>
212 212
          {% for email in emails %}
7  docs/topics/forms/index.txt
@@ -371,12 +371,11 @@ using the :ttag:`include` tag to reuse it in other templates::
371 371
     {% endfor %}
372 372
 
373 373
 If the form object passed to a template has a different name within the
374  
-context, you can alias it using the :ttag:`with` tag::
  374
+context, you can alias it using the ``with`` argument of the :ttag:`include`
  375
+tag::
375 376
 
376 377
     <form action="/comments/add/" method="post">
377  
-        {% with comment_form as form %}
378  
-            {% include "form_snippet.html" %}
379  
-        {% endwith %}
  378
+        {% include "form_snippet.html" with form=comment_form %}
380 379
         <p><input type="submit" value="Submit comment" /></p>
381 380
     </form>
382 381
 
16  docs/topics/i18n/internationalization.txt
@@ -438,6 +438,9 @@ It's not possible to mix a template variable inside a string within ``{% trans
438 438
 ``blocktrans`` template tag
439 439
 ---------------------------
440 440
 
  441
+.. versionchanged:: 1.3
  442
+   New keyword argument format.
  443
+
441 444
 Contrarily to the ``trans`` tag, the ``blocktrans`` tag allows you to mark
442 445
 complex sentences consisting of literals and variable content for translation
443 446
 by making use of placeholders::
@@ -448,18 +451,18 @@ To translate a template expression -- say, accessing object attributes or
448 451
 using template filters -- you need to bind the expression to a local variable
449 452
 for use within the translation block. Examples::
450 453
 
451  
-    {% blocktrans with article.price as amount %}
  454
+    {% blocktrans with amount=article.price %}
452 455
     That will cost $ {{ amount }}.
453 456
     {% endblocktrans %}
454 457
 
455  
-    {% blocktrans with value|filter as myvar %}
  458
+    {% blocktrans with myvar=value|filter %}
456 459
     This will have {{ myvar }} inside.
457 460
     {% endblocktrans %}
458 461
 
459 462
 If you need to bind more than one expression inside a ``blocktrans`` tag,
460 463
 separate the pieces with ``and``::
461 464
 
462  
-    {% blocktrans with book|title as book_t and author|title as author_t %}
  465
+    {% blocktrans with book_t=book|title author_t=author|title %}
463 466
     This is {{ book_t }} by {{ author_t }}
464 467
     {% endblocktrans %}
465 468
 
@@ -474,7 +477,7 @@ This tag also provides for pluralization. To use it:
474 477
 
475 478
 An example::
476 479
 
477  
-    {% blocktrans count list|length as counter %}
  480
+    {% blocktrans count counter=list|length %}
478 481
     There is only one {{ name }} object.
479 482
     {% plural %}
480 483
     There are {{ counter }} {{ name }} objects.
@@ -482,7 +485,7 @@ An example::
482 485
 
483 486
 A more complex example::
484 487
 
485  
-    {% blocktrans with article.price as amount count i.length as years %}
  488
+    {% blocktrans with amount=article.price count years=i.length %}
486 489
     That will cost $ {{ amount }} per year.
487 490
     {% plural %}
488 491
     That will cost $ {{ amount }} per {{ years }} years.
@@ -494,6 +497,9 @@ construct is internally converted to an ``ungettext`` call. This means the
494 497
 same :ref:`notes regarding ungettext variables <pluralization-var-notes>`
495 498
 apply.
496 499
 
  500
+.. note:: The previous more verbose format is still supported:
  501
+   ``{% blocktrans with book|title as book_t and author|title as author_t %}``
  502
+
497 503
 .. _template-translation-vars:
498 504
 
499 505
 Other tags
55  tests/regressiontests/templates/tests.py
@@ -930,7 +930,7 @@ def get_template_tests(self):
930 930
             'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
931 931
             'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"),
932 932
 
933  
-            ### INCLUDE TAG ###########################################################
  933
+            ## INCLUDE TAG ###########################################################
934 934
             'include01': ('{% include "basic-syntax01" %}', {}, "something cool"),
935 935
             'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
936 936
             'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
@@ -938,6 +938,23 @@ def get_template_tests(self):
938 938
             'include 05': ('template with a space', {}, 'template with a space'),
939 939
             'include06': ('{% include "include 05"%}', {}, 'template with a space'),
940 940
 
  941
+            # extra inline context
  942
+            'include07': ('{% include "basic-syntax02" with headline="Inline" %}', {'headline': 'Included'}, 'Inline'),
  943
+            'include08': ('{% include headline with headline="Dynamic" %}', {'headline': 'basic-syntax02'}, 'Dynamic'),
  944
+            'include09': ('{{ first }}--{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}--{{ second }}', {'first': 'Ul', 'second': 'lU'}, 'Ul--LU --- UL--lU'),
  945
+
  946
+            # isolated context
  947
+            'include10': ('{% include "basic-syntax03" only %}', {'first': '1'}, (' --- ', 'INVALID --- INVALID')),
  948
+            'include11': ('{% include "basic-syntax03" only with second=2 %}', {'first': '1'}, (' --- 2', 'INVALID --- 2')),
  949
+            'include12': ('{% include "basic-syntax03" with first=1 only %}', {'second': '2'}, ('1 --- ', '1 --- INVALID')),
  950
+
  951
+            'include-error01': ('{% include "basic-syntax01" with %}', {}, template.TemplateSyntaxError),
  952
+            'include-error02': ('{% include "basic-syntax01" with "no key" %}', {}, template.TemplateSyntaxError),
  953
+            'include-error03': ('{% include "basic-syntax01" with dotted.arg="error" %}', {}, template.TemplateSyntaxError),
  954
+            'include-error04': ('{% include "basic-syntax01" something_random %}', {}, template.TemplateSyntaxError),
  955
+            'include-error05': ('{% include "basic-syntax01" foo="duplicate" foo="key" %}', {}, template.TemplateSyntaxError),
  956
+            'include-error06': ('{% include "basic-syntax01" only only %}', {}, template.TemplateSyntaxError),
  957
+
941 958
             ### NAMED ENDBLOCKS #######################################################
942 959
 
943 960
             # Basic test
@@ -1098,7 +1115,8 @@ def get_template_tests(self):
1098 1115
             'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u"Å"),
1099 1116
 
1100 1117
             # simple translation of a variable and filter
1101  
-            'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
  1118
+            'i18n04': ('{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
  1119
+            'legacyi18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
1102 1120
 
1103 1121
             # simple translation of a string with interpolation
1104 1122
             'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
@@ -1107,10 +1125,12 @@ def get_template_tests(self):
1107 1125
             'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"),
1108 1126
 
1109 1127
             # translation of singular form
1110  
-            'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
  1128
+            'i18n07': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
  1129
+            'legacyi18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
1111 1130
 
1112 1131
             # translation of plural form
1113 1132
             'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"),
  1133
+            'legacyi18n08': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"),
1114 1134
 
1115 1135
             # simple non-translation (only marking) of a string to german
1116 1136
             'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
@@ -1132,12 +1152,14 @@ def get_template_tests(self):
1132 1152
 
1133 1153
             # Escaping inside blocktrans and trans works as if it was directly in the
1134 1154
             # template.
1135  
-            'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
1136  
-            'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
  1155
+            'i18n17': ('{% load i18n %}{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
  1156
+            'i18n18': ('{% load i18n %}{% blocktrans with berta=anton|force_escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
1137 1157
             'i18n19': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': 'a & b'}, u'a &amp; b'),
1138 1158
             'i18n20': ('{% load i18n %}{% trans andrew %}', {'andrew': 'a & b'}, u'a &amp; b'),
1139 1159
             'i18n21': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': mark_safe('a & b')}, u'a & b'),
1140 1160
             'i18n22': ('{% load i18n %}{% trans andrew %}', {'andrew': mark_safe('a & b')}, u'a & b'),
  1161
+            'legacyi18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
  1162
+            'legacyi18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
1141 1163
 
1142 1164
             # Use filters with the {% trans %} tag, #5972
1143 1165
             'i18n23': ('{% load i18n %}{% trans "Page not found"|capfirst|slice:"6:" %}', {'LANGUAGE_CODE': 'de'}, u'nicht gefunden'),
@@ -1145,10 +1167,16 @@ def get_template_tests(self):
1145 1167
             'i18n25': ('{% load i18n %}{% trans somevar|upper %}', {'somevar': 'Page not found', 'LANGUAGE_CODE': 'de'}, u'SEITE NICHT GEFUNDEN'),
1146 1168
 
1147 1169
             # translation of plural form with extra field in singular form (#13568)
1148  
-            'i18n26': ('{% load i18n %}{% blocktrans with myextra_field as extra_field count number as counter %}singular {{ extra_field }}{% plural %}plural{% endblocktrans %}', {'number': 1, 'myextra_field': 'test'}, "singular test"),
  1170
+            'i18n26': ('{% load i18n %}{% blocktrans with extra_field=myextra_field count counter=number %}singular {{ extra_field }}{% plural %}plural{% endblocktrans %}', {'number': 1, 'myextra_field': 'test'}, "singular test"),
  1171
+            'legacyi18n26': ('{% load i18n %}{% blocktrans with myextra_field as extra_field count number as counter %}singular {{ extra_field }}{% plural %}plural{% endblocktrans %}', {'number': 1, 'myextra_field': 'test'}, "singular test"),
1149 1172
 
1150 1173
             # translation of singular form in russian (#14126)
1151  
-            'i18n27': ('{% load i18n %}{% blocktrans count number as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, u'1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'),
  1174
+            'i18n27': ('{% load i18n %}{% blocktrans count counter=number %}1 result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, u'1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'),
  1175
+            'legacyi18n27': ('{% load i18n %}{% blocktrans count number as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, u'1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'),
  1176
+
  1177
+            # simple translation of multiple variables
  1178
+            'i18n28': ('{% load i18n %}{% blocktrans with a=anton b=berta %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, u'α + β'),
  1179
+            'legacyi18n28': ('{% load i18n %}{% blocktrans with anton as a and berta as b %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, u'α + β'),
1152 1180
 
1153 1181
             # retrieving language information
1154 1182
             'i18n28': ('{% load i18n %}{% get_language_info for "de" as l %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {}, 'de: German/Deutsch bidi=False'),
@@ -1273,11 +1301,16 @@ def get_template_tests(self):
1273 1301
             'widthratio11': ('{% widthratio a b c %}', {'a':50,'b':100, 'c': 100}, '50'),
1274 1302
 
1275 1303
             ### WITH TAG ########################################################
1276  
-            'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'),
1277  
-            'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
  1304
+            'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
  1305
+            'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
  1306
+
  1307
+            'with02': ('{{ key }}{% with key=dict.key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
  1308
+            'legacywith02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
  1309
+
  1310
+            'with03': ('{% with a=alpha b=beta %}{{ a }}{{ b }}{% endwith %}', {'alpha': 'A', 'beta': 'B'}, 'AB'),
1278 1311
 
1279  
-            'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
1280  
-            'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
  1312
+            'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError),
  1313
+            'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError),
1281 1314
 
1282 1315
             ### NOW TAG ########################################################
1283 1316
             # Simple case

0 notes on commit 3ae9117

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