- 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
This commit is contained in:
parent
99742d8d73
commit
3ae9117c46
|
@ -16,6 +16,55 @@ register = Library()
|
|||
# Regex for token keyword arguments
|
||||
kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")
|
||||
|
||||
def token_kwargs(bits, parser, support_legacy=False):
|
||||
"""
|
||||
A utility method for parsing token keyword arguments.
|
||||
|
||||
:param bits: A list containing remainder of the token (split by spaces)
|
||||
that is to be checked for arguments. Valid arguments will be removed
|
||||
from this list.
|
||||
|
||||
:param support_legacy: If set to true ``True``, the legacy format
|
||||
``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1``
|
||||
format is allowed.
|
||||
|
||||
:returns: A dictionary of the arguments retrieved from the ``bits`` token
|
||||
list.
|
||||
|
||||
There is no requirement for all remaining token ``bits`` to be keyword
|
||||
arguments, so the dictionary will be returned as soon as an invalid
|
||||
argument format is reached.
|
||||
"""
|
||||
if not bits:
|
||||
return {}
|
||||
match = kwarg_re.match(bits[0])
|
||||
kwarg_format = match and match.group(1)
|
||||
if not kwarg_format:
|
||||
if not support_legacy:
|
||||
return {}
|
||||
if len(bits) < 3 or bits[1] != 'as':
|
||||
return {}
|
||||
|
||||
kwargs = {}
|
||||
while bits:
|
||||
if kwarg_format:
|
||||
match = kwarg_re.match(bits[0])
|
||||
if not match or not match.group(1):
|
||||
return kwargs
|
||||
key, value = match.groups()
|
||||
del bits[:1]
|
||||
else:
|
||||
if len(bits) < 3 or bits[1] != 'as':
|
||||
return kwargs
|
||||
key, value = bits[2], bits[0]
|
||||
del bits[:3]
|
||||
kwargs[key] = parser.compile_filter(value)
|
||||
if bits and not kwarg_format:
|
||||
if bits[0] != 'and':
|
||||
return kwargs
|
||||
del bits[:1]
|
||||
return kwargs
|
||||
|
||||
class AutoEscapeControlNode(Node):
|
||||
"""Implements the actions of the autoescape tag."""
|
||||
def __init__(self, setting, nodelist):
|
||||
|
@ -298,7 +347,7 @@ class SsiNode(Node):
|
|||
def render(self, context):
|
||||
filepath = self.filepath
|
||||
if not self.legacy_filepath:
|
||||
filepath = filepath.resolve(context)
|
||||
filepath = filepath.resolve(context)
|
||||
|
||||
if not include_is_allowed(filepath):
|
||||
if settings.DEBUG:
|
||||
|
@ -433,18 +482,25 @@ class WidthRatioNode(Node):
|
|||
return str(int(round(ratio)))
|
||||
|
||||
class WithNode(Node):
|
||||
def __init__(self, var, name, nodelist):
|
||||
self.var = var
|
||||
self.name = name
|
||||
def __init__(self, var, name, nodelist, extra_context=None,
|
||||
isolated_context=False):
|
||||
self.nodelist = nodelist
|
||||
# var and name are legacy attributes, being left in case they are used
|
||||
# by third-party subclasses of this Node.
|
||||
self.extra_context = extra_context or {}
|
||||
if name:
|
||||
self.extra_context[name] = var
|
||||
self.isolated_context = isolated_context
|
||||
|
||||
def __repr__(self):
|
||||
return "<WithNode>"
|
||||
|
||||
def render(self, context):
|
||||
val = self.var.resolve(context)
|
||||
context.push()
|
||||
context[self.name] = val
|
||||
values = dict([(key, val.resolve(context)) for key, val in
|
||||
self.extra_context.iteritems()])
|
||||
if self.isolated_context:
|
||||
return self.nodelist.render(Context(values))
|
||||
context.update(values)
|
||||
output = self.nodelist.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
@ -1276,22 +1332,34 @@ widthratio = register.tag(widthratio)
|
|||
#@register.tag
|
||||
def do_with(parser, token):
|
||||
"""
|
||||
Adds a value to the context (inside of this block) for caching and easy
|
||||
access.
|
||||
Adds one or more values to the context (inside of this block) for caching
|
||||
and easy access.
|
||||
|
||||
For example::
|
||||
|
||||
{% with person.some_sql_method as total %}
|
||||
{% with total=person.some_sql_method %}
|
||||
{{ total }} object{{ total|pluralize }}
|
||||
{% endwith %}
|
||||
|
||||
Multiple values can be added to the context::
|
||||
|
||||
{% with foo=1 bar=2 %}
|
||||
...
|
||||
{% endwith %}
|
||||
|
||||
The legacy format of ``{% with person.some_sql_method as total %}`` is
|
||||
still accepted.
|
||||
"""
|
||||
bits = list(token.split_contents())
|
||||
if len(bits) != 4 or bits[2] != "as":
|
||||
raise TemplateSyntaxError("%r expected format is 'value as name'" %
|
||||
bits[0])
|
||||
var = parser.compile_filter(bits[1])
|
||||
name = bits[3]
|
||||
bits = token.split_contents()
|
||||
remaining_bits = bits[1:]
|
||||
extra_context = token_kwargs(remaining_bits, parser, support_legacy=True)
|
||||
if not extra_context:
|
||||
raise TemplateSyntaxError("%r expected at least one variable "
|
||||
"assignment" % bits[0])
|
||||
if remaining_bits:
|
||||
raise TemplateSyntaxError("%r received an invalid token: %r" %
|
||||
(bits[0], remaining_bits[0]))
|
||||
nodelist = parser.parse(('endwith',))
|
||||
parser.delete_first_token()
|
||||
return WithNode(var, name, nodelist)
|
||||
return WithNode(None, None, nodelist, extra_context=extra_context)
|
||||
do_with = register.tag('with', do_with)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.template.base import TemplateSyntaxError, TemplateDoesNotExist, Variable
|
||||
from django.template.base import Library, Node, TextNode
|
||||
from django.template.context import Context
|
||||
from django.template.defaulttags import token_kwargs
|
||||
from django.template.loader import get_template
|
||||
from django.conf import settings
|
||||
from django.utils.safestring import mark_safe
|
||||
|
@ -124,8 +126,25 @@ class ExtendsNode(Node):
|
|||
# the same.
|
||||
return compiled_parent._render(context)
|
||||
|
||||
class ConstantIncludeNode(Node):
|
||||
def __init__(self, template_path):
|
||||
class BaseIncludeNode(Node):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.extra_context = kwargs.pop('extra_context', {})
|
||||
self.isolated_context = kwargs.pop('isolated_context', False)
|
||||
super(BaseIncludeNode, self).__init__(*args, **kwargs)
|
||||
|
||||
def render_template(self, template, context):
|
||||
values = dict([(name, var.resolve(context)) for name, var
|
||||
in self.extra_context.iteritems()])
|
||||
if self.isolated_context:
|
||||
return template.render(Context(values))
|
||||
context.update(values)
|
||||
output = template.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
||||
class ConstantIncludeNode(BaseIncludeNode):
|
||||
def __init__(self, template_path, *args, **kwargs):
|
||||
super(ConstantIncludeNode, self).__init__(*args, **kwargs)
|
||||
try:
|
||||
t = get_template(template_path)
|
||||
self.template = t
|
||||
|
@ -135,21 +154,21 @@ class ConstantIncludeNode(Node):
|
|||
self.template = None
|
||||
|
||||
def render(self, context):
|
||||
if self.template:
|
||||
return self.template.render(context)
|
||||
else:
|
||||
if not self.template:
|
||||
return ''
|
||||
return self.render_template(self.template, context)
|
||||
|
||||
class IncludeNode(Node):
|
||||
def __init__(self, template_name):
|
||||
self.template_name = Variable(template_name)
|
||||
class IncludeNode(BaseIncludeNode):
|
||||
def __init__(self, template_name, *args, **kwargs):
|
||||
super(IncludeNode, self).__init__(*args, **kwargs)
|
||||
self.template_name = template_name
|
||||
|
||||
def render(self, context):
|
||||
try:
|
||||
template_name = self.template_name.resolve(context)
|
||||
t = get_template(template_name)
|
||||
return t.render(context)
|
||||
except TemplateSyntaxError, e:
|
||||
template = get_template(template_name)
|
||||
return self.render_template(template, context)
|
||||
except TemplateSyntaxError:
|
||||
if settings.TEMPLATE_DEBUG:
|
||||
raise
|
||||
return ''
|
||||
|
@ -201,19 +220,49 @@ def do_extends(parser, token):
|
|||
|
||||
def do_include(parser, token):
|
||||
"""
|
||||
Loads a template and renders it with the current context.
|
||||
Loads a template and renders it with the current context. You can pass
|
||||
additional context using keyword arguments.
|
||||
|
||||
Example::
|
||||
|
||||
{% include "foo/some_include" %}
|
||||
{% include "foo/some_include" with bar="BAZZ!" baz="BING!" %}
|
||||
|
||||
Use the ``only`` argument to exclude the current context when rendering
|
||||
the included template::
|
||||
|
||||
{% include "foo/some_include" only %}
|
||||
{% include "foo/some_include" with bar="1" only %}
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
if len(bits) != 2:
|
||||
raise TemplateSyntaxError("%r tag takes one argument: the name of the template to be included" % bits[0])
|
||||
if len(bits) < 2:
|
||||
raise TemplateSyntaxError("%r tag takes at least one argument: the name of the template to be included." % bits[0])
|
||||
options = {}
|
||||
remaining_bits = bits[2:]
|
||||
while remaining_bits:
|
||||
option = remaining_bits.pop(0)
|
||||
if option in options:
|
||||
raise TemplateSyntaxError('The %r option was specified more '
|
||||
'than once.' % option)
|
||||
if option == 'with':
|
||||
value = token_kwargs(remaining_bits, parser, support_legacy=False)
|
||||
if not value:
|
||||
raise TemplateSyntaxError('"with" in %r tag needs at least '
|
||||
'one keyword argument.' % bits[0])
|
||||
elif option == 'only':
|
||||
value = True
|
||||
else:
|
||||
raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
|
||||
(bits[0], option))
|
||||
options[option] = value
|
||||
isolated_context = options.get('only', False)
|
||||
namemap = options.get('with', {})
|
||||
path = bits[1]
|
||||
if path[0] in ('"', "'") and path[-1] == path[0]:
|
||||
return ConstantIncludeNode(path[1:-1])
|
||||
return IncludeNode(bits[1])
|
||||
return ConstantIncludeNode(path[1:-1], extra_context=namemap,
|
||||
isolated_context=isolated_context)
|
||||
return IncludeNode(parser.compile_filter(bits[1]), extra_context=namemap,
|
||||
isolated_context=isolated_context)
|
||||
|
||||
register.tag('block', do_block)
|
||||
register.tag('extends', do_extends)
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.template import TOKEN_TEXT, TOKEN_VAR
|
|||
from django.template.base import _render_value_in_context
|
||||
from django.utils import translation
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.template.defaulttags import token_kwargs
|
||||
|
||||
register = Library()
|
||||
|
||||
|
@ -97,7 +98,7 @@ class BlockTranslateNode(Node):
|
|||
def render(self, context):
|
||||
tmp_context = {}
|
||||
for var, val in self.extra_context.items():
|
||||
tmp_context[var] = val.render(context)
|
||||
tmp_context[var] = val.resolve(context)
|
||||
# Update() works like a push(), so corresponding context.pop() is at
|
||||
# the end of function
|
||||
context.update(tmp_context)
|
||||
|
@ -284,43 +285,54 @@ def do_block_translate(parser, token):
|
|||
|
||||
Usage::
|
||||
|
||||
{% blocktrans with foo|filter as bar and baz|filter as boo %}
|
||||
{% blocktrans with bar=foo|filter boo=baz|filter %}
|
||||
This is {{ bar }} and {{ boo }}.
|
||||
{% endblocktrans %}
|
||||
|
||||
Additionally, this supports pluralization::
|
||||
|
||||
{% blocktrans count var|length as count %}
|
||||
{% blocktrans count count=var|length %}
|
||||
There is {{ count }} object.
|
||||
{% plural %}
|
||||
There are {{ count }} objects.
|
||||
{% endblocktrans %}
|
||||
|
||||
This is much like ngettext, only in template syntax.
|
||||
"""
|
||||
class BlockTranslateParser(TokenParser):
|
||||
def top(self):
|
||||
countervar = None
|
||||
counter = None
|
||||
extra_context = {}
|
||||
while self.more():
|
||||
tag = self.tag()
|
||||
if tag == 'with' or tag == 'and':
|
||||
value = self.value()
|
||||
if self.tag() != 'as':
|
||||
raise TemplateSyntaxError("variable bindings in 'blocktrans' must be 'with value as variable'")
|
||||
extra_context[self.tag()] = VariableNode(
|
||||
parser.compile_filter(value))
|
||||
elif tag == 'count':
|
||||
counter = parser.compile_filter(self.value())
|
||||
if self.tag() != 'as':
|
||||
raise TemplateSyntaxError("counter specification in 'blocktrans' must be 'count value as variable'")
|
||||
countervar = self.tag()
|
||||
else:
|
||||
raise TemplateSyntaxError("unknown subtag %s for 'blocktrans' found" % tag)
|
||||
return (countervar, counter, extra_context)
|
||||
|
||||
countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
|
||||
The "var as value" legacy format is still supported::
|
||||
|
||||
{% blocktrans with foo|filter as bar and baz|filter as boo %}
|
||||
{% blocktrans count var|length as count %}
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
|
||||
options = {}
|
||||
remaining_bits = bits[1:]
|
||||
while remaining_bits:
|
||||
option = remaining_bits.pop(0)
|
||||
if option in options:
|
||||
raise TemplateSyntaxError('The %r option was specified more '
|
||||
'than once.' % option)
|
||||
if option == 'with':
|
||||
value = token_kwargs(remaining_bits, parser, support_legacy=True)
|
||||
if not value:
|
||||
raise TemplateSyntaxError('"with" in %r tag needs at least '
|
||||
'one keyword argument.' % bits[0])
|
||||
elif option == 'count':
|
||||
value = token_kwargs(remaining_bits, parser, support_legacy=True)
|
||||
if len(value) != 1:
|
||||
raise TemplateSyntaxError('"count" in %r tag expected exactly '
|
||||
'one keyword argument.' % bits[0])
|
||||
else:
|
||||
raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
|
||||
(bits[0], option))
|
||||
options[option] = value
|
||||
|
||||
if 'count' in options:
|
||||
countervar, counter = options['count'].items()[0]
|
||||
else:
|
||||
countervar, counter = None, None
|
||||
extra_context = options.get('with', {})
|
||||
|
||||
singular = []
|
||||
plural = []
|
||||
|
|
|
@ -639,9 +639,19 @@ including it. This example produces the output ``"Hello, John"``:
|
|||
|
||||
* The ``name_snippet.html`` template::
|
||||
|
||||
Hello, {{ person }}
|
||||
{{ greeting }}, {{ person|default:"friend" }}!
|
||||
|
||||
See also: ``{% ssi %}``.
|
||||
.. versionchanged:: 1.3
|
||||
Additional context and exclusive context.
|
||||
|
||||
You can pass additional context to the template using keyword arguments::
|
||||
|
||||
{% include "name_snippet.html" with person="Jane" greeting="Hello" "%}
|
||||
|
||||
If you want to only render the context with the variables provided (or even
|
||||
no variables at all), use the ``only`` option::
|
||||
|
||||
{% include "name_snippet.html" with greeting="Hi" only %}
|
||||
|
||||
.. note::
|
||||
The :ttag:`include` tag should be considered as an implementation of
|
||||
|
@ -650,6 +660,8 @@ See also: ``{% ssi %}``.
|
|||
This means that there is no shared state between included templates --
|
||||
each include is a completely independent rendering process.
|
||||
|
||||
See also: ``{% ssi %}``.
|
||||
|
||||
.. templatetag:: load
|
||||
|
||||
load
|
||||
|
@ -1044,18 +1056,30 @@ with
|
|||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
New keyword argument format and multiple variable assignments.
|
||||
|
||||
Caches a complex variable under a simpler name. This is useful when accessing
|
||||
an "expensive" method (e.g., one that hits the database) multiple times.
|
||||
|
||||
For example::
|
||||
|
||||
{% with business.employees.count as total %}
|
||||
{% with total=business.employees.count %}
|
||||
{{ total }} employee{{ total|pluralize }}
|
||||
{% endwith %}
|
||||
|
||||
The populated variable (in the example above, ``total``) is only available
|
||||
between the ``{% with %}`` and ``{% endwith %}`` tags.
|
||||
|
||||
You can assign more than one context variable::
|
||||
|
||||
{% with alpha=1 beta=2 %}
|
||||
...
|
||||
{% endwith %}
|
||||
|
||||
.. note:: The previous more verbose format is still supported:
|
||||
``{% with business.employees.count as total %}``
|
||||
|
||||
.. _ref-templates-builtins-filters:
|
||||
|
||||
Built-in filter reference
|
||||
|
|
|
@ -206,7 +206,7 @@ many-to-many relation to User, the following template code is optimal:
|
|||
.. code-block:: html+django
|
||||
|
||||
{% if display_inbox %}
|
||||
{% with user.emails.all as emails %}
|
||||
{% with emails=user.emails.all %}
|
||||
{% if emails %}
|
||||
<p>You have {{ emails|length }} email(s)</p>
|
||||
{% for email in emails %}
|
||||
|
|
|
@ -371,12 +371,11 @@ using the :ttag:`include` tag to reuse it in other templates::
|
|||
{% endfor %}
|
||||
|
||||
If the form object passed to a template has a different name within the
|
||||
context, you can alias it using the :ttag:`with` tag::
|
||||
context, you can alias it using the ``with`` argument of the :ttag:`include`
|
||||
tag::
|
||||
|
||||
<form action="/comments/add/" method="post">
|
||||
{% with comment_form as form %}
|
||||
{% include "form_snippet.html" %}
|
||||
{% endwith %}
|
||||
{% include "form_snippet.html" with form=comment_form %}
|
||||
<p><input type="submit" value="Submit comment" /></p>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -438,6 +438,9 @@ It's not possible to mix a template variable inside a string within ``{% trans
|
|||
``blocktrans`` template tag
|
||||
---------------------------
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
New keyword argument format.
|
||||
|
||||
Contrarily to the ``trans`` tag, the ``blocktrans`` tag allows you to mark
|
||||
complex sentences consisting of literals and variable content for translation
|
||||
by making use of placeholders::
|
||||
|
@ -448,18 +451,18 @@ To translate a template expression -- say, accessing object attributes or
|
|||
using template filters -- you need to bind the expression to a local variable
|
||||
for use within the translation block. Examples::
|
||||
|
||||
{% blocktrans with article.price as amount %}
|
||||
{% blocktrans with amount=article.price %}
|
||||
That will cost $ {{ amount }}.
|
||||
{% endblocktrans %}
|
||||
|
||||
{% blocktrans with value|filter as myvar %}
|
||||
{% blocktrans with myvar=value|filter %}
|
||||
This will have {{ myvar }} inside.
|
||||
{% endblocktrans %}
|
||||
|
||||
If you need to bind more than one expression inside a ``blocktrans`` tag,
|
||||
separate the pieces with ``and``::
|
||||
|
||||
{% blocktrans with book|title as book_t and author|title as author_t %}
|
||||
{% blocktrans with book_t=book|title author_t=author|title %}
|
||||
This is {{ book_t }} by {{ author_t }}
|
||||
{% endblocktrans %}
|
||||
|
||||
|
@ -474,7 +477,7 @@ This tag also provides for pluralization. To use it:
|
|||
|
||||
An example::
|
||||
|
||||
{% blocktrans count list|length as counter %}
|
||||
{% blocktrans count counter=list|length %}
|
||||
There is only one {{ name }} object.
|
||||
{% plural %}
|
||||
There are {{ counter }} {{ name }} objects.
|
||||
|
@ -482,7 +485,7 @@ An example::
|
|||
|
||||
A more complex example::
|
||||
|
||||
{% blocktrans with article.price as amount count i.length as years %}
|
||||
{% blocktrans with amount=article.price count years=i.length %}
|
||||
That will cost $ {{ amount }} per year.
|
||||
{% plural %}
|
||||
That will cost $ {{ amount }} per {{ years }} years.
|
||||
|
@ -494,6 +497,9 @@ construct is internally converted to an ``ungettext`` call. This means the
|
|||
same :ref:`notes regarding ungettext variables <pluralization-var-notes>`
|
||||
apply.
|
||||
|
||||
.. note:: The previous more verbose format is still supported:
|
||||
``{% blocktrans with book|title as book_t and author|title as author_t %}``
|
||||
|
||||
.. _template-translation-vars:
|
||||
|
||||
Other tags
|
||||
|
|
|
@ -930,7 +930,7 @@ class Templates(unittest.TestCase):
|
|||
'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
|
||||
'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"),
|
||||
|
||||
### INCLUDE TAG ###########################################################
|
||||
## INCLUDE TAG ###########################################################
|
||||
'include01': ('{% include "basic-syntax01" %}', {}, "something cool"),
|
||||
'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
|
||||
'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
|
||||
|
@ -938,6 +938,23 @@ class Templates(unittest.TestCase):
|
|||
'include 05': ('template with a space', {}, 'template with a space'),
|
||||
'include06': ('{% include "include 05"%}', {}, 'template with a space'),
|
||||
|
||||
# extra inline context
|
||||
'include07': ('{% include "basic-syntax02" with headline="Inline" %}', {'headline': 'Included'}, 'Inline'),
|
||||
'include08': ('{% include headline with headline="Dynamic" %}', {'headline': 'basic-syntax02'}, 'Dynamic'),
|
||||
'include09': ('{{ first }}--{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}--{{ second }}', {'first': 'Ul', 'second': 'lU'}, 'Ul--LU --- UL--lU'),
|
||||
|
||||
# isolated context
|
||||
'include10': ('{% include "basic-syntax03" only %}', {'first': '1'}, (' --- ', 'INVALID --- INVALID')),
|
||||
'include11': ('{% include "basic-syntax03" only with second=2 %}', {'first': '1'}, (' --- 2', 'INVALID --- 2')),
|
||||
'include12': ('{% include "basic-syntax03" with first=1 only %}', {'second': '2'}, ('1 --- ', '1 --- INVALID')),
|
||||
|
||||
'include-error01': ('{% include "basic-syntax01" with %}', {}, template.TemplateSyntaxError),
|
||||
'include-error02': ('{% include "basic-syntax01" with "no key" %}', {}, template.TemplateSyntaxError),
|
||||
'include-error03': ('{% include "basic-syntax01" with dotted.arg="error" %}', {}, template.TemplateSyntaxError),
|
||||
'include-error04': ('{% include "basic-syntax01" something_random %}', {}, template.TemplateSyntaxError),
|
||||
'include-error05': ('{% include "basic-syntax01" foo="duplicate" foo="key" %}', {}, template.TemplateSyntaxError),
|
||||
'include-error06': ('{% include "basic-syntax01" only only %}', {}, template.TemplateSyntaxError),
|
||||
|
||||
### NAMED ENDBLOCKS #######################################################
|
||||
|
||||
# Basic test
|
||||
|
@ -1098,7 +1115,8 @@ class Templates(unittest.TestCase):
|
|||
'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u"Å"),
|
||||
|
||||
# simple translation of a variable and filter
|
||||
'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
|
||||
'i18n04': ('{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
|
||||
'legacyi18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
|
||||
|
||||
# simple translation of a string with interpolation
|
||||
'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
|
||||
|
@ -1107,10 +1125,12 @@ class Templates(unittest.TestCase):
|
|||
'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"),
|
||||
|
||||
# translation of singular form
|
||||
'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
|
||||
'i18n07': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
|
||||
'legacyi18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
|
||||
|
||||
# translation of plural form
|
||||
'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"),
|
||||
'legacyi18n08': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"),
|
||||
|
||||
# simple non-translation (only marking) of a string to german
|
||||
'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
|
||||
|
@ -1132,12 +1152,14 @@ class Templates(unittest.TestCase):
|
|||
|
||||
# Escaping inside blocktrans and trans works as if it was directly in the
|
||||
# template.
|
||||
'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
'i18n17': ('{% load i18n %}{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
'i18n18': ('{% load i18n %}{% blocktrans with berta=anton|force_escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
'i18n19': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': 'a & b'}, u'a & b'),
|
||||
'i18n20': ('{% load i18n %}{% trans andrew %}', {'andrew': 'a & b'}, u'a & b'),
|
||||
'i18n21': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': mark_safe('a & b')}, u'a & b'),
|
||||
'i18n22': ('{% load i18n %}{% trans andrew %}', {'andrew': mark_safe('a & b')}, u'a & b'),
|
||||
'legacyi18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
'legacyi18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
|
||||
|
||||
# Use filters with the {% trans %} tag, #5972
|
||||
'i18n23': ('{% load i18n %}{% trans "Page not found"|capfirst|slice:"6:" %}', {'LANGUAGE_CODE': 'de'}, u'nicht gefunden'),
|
||||
|
@ -1145,10 +1167,16 @@ class Templates(unittest.TestCase):
|
|||
'i18n25': ('{% load i18n %}{% trans somevar|upper %}', {'somevar': 'Page not found', 'LANGUAGE_CODE': 'de'}, u'SEITE NICHT GEFUNDEN'),
|
||||
|
||||
# translation of plural form with extra field in singular form (#13568)
|
||||
'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"),
|
||||
'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"),
|
||||
'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"),
|
||||
|
||||
# translation of singular form in russian (#14126)
|
||||
'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'),
|
||||
'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'),
|
||||
'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'),
|
||||
|
||||
# simple translation of multiple variables
|
||||
'i18n28': ('{% load i18n %}{% blocktrans with a=anton b=berta %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, u'α + β'),
|
||||
'legacyi18n28': ('{% load i18n %}{% blocktrans with anton as a and berta as b %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, u'α + β'),
|
||||
|
||||
# retrieving language information
|
||||
'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 @@ class Templates(unittest.TestCase):
|
|||
'widthratio11': ('{% widthratio a b c %}', {'a':50,'b':100, 'c': 100}, '50'),
|
||||
|
||||
### WITH TAG ########################################################
|
||||
'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'),
|
||||
'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
|
||||
'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
|
||||
'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
|
||||
|
||||
'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
|
||||
'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
|
||||
'with02': ('{{ key }}{% with key=dict.key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
|
||||
'legacywith02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
|
||||
|
||||
'with03': ('{% with a=alpha b=beta %}{{ a }}{{ b }}{% endwith %}', {'alpha': 'A', 'beta': 'B'}, 'AB'),
|
||||
|
||||
'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError),
|
||||
'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError),
|
||||
|
||||
### NOW TAG ########################################################
|
||||
# Simple case
|
||||
|
|
Loading…
Reference in New Issue