mirror of
https://github.com/django/django.git
synced 2025-10-30 17:16:10 +00:00
- 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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user