Fixed #3453: introduced a new template variable resolution system by Brian Harring (thanks!). The upshot is that variable resolution is about 25% faster, and you should see a measurable performance increase any time you've got long or deeply nested loops.
Variable resolution has changed behind the scenes -- see the note in templates_python.txt -- but template.resolve_variable() still exists. This should be fully backwards-compatible. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6399 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
75e462099b
commit
2570954a9a
|
@ -72,7 +72,7 @@ class FieldWidgetNode(template.Node):
|
||||||
default = None
|
default = None
|
||||||
|
|
||||||
def __init__(self, bound_field_var):
|
def __init__(self, bound_field_var):
|
||||||
self.bound_field_var = bound_field_var
|
self.bound_field_var = template.Variable(bound_field_var)
|
||||||
|
|
||||||
def get_nodelist(cls, klass):
|
def get_nodelist(cls, klass):
|
||||||
if klass not in cls.nodelists:
|
if klass not in cls.nodelists:
|
||||||
|
@ -96,7 +96,7 @@ class FieldWidgetNode(template.Node):
|
||||||
get_nodelist = classmethod(get_nodelist)
|
get_nodelist = classmethod(get_nodelist)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
bound_field = template.resolve_variable(self.bound_field_var, context)
|
bound_field = self.bound_field_var.resolve(context)
|
||||||
|
|
||||||
context.push()
|
context.push()
|
||||||
context['bound_field'] = bound_field
|
context['bound_field'] = bound_field
|
||||||
|
@ -156,10 +156,10 @@ class StackedBoundRelatedObject(BoundRelatedObject):
|
||||||
|
|
||||||
class EditInlineNode(template.Node):
|
class EditInlineNode(template.Node):
|
||||||
def __init__(self, rel_var):
|
def __init__(self, rel_var):
|
||||||
self.rel_var = rel_var
|
self.rel_var = template.Variable(rel_var)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
relation = template.resolve_variable(self.rel_var, context)
|
relation = self.rel_var.resolve(context)
|
||||||
context.push()
|
context.push()
|
||||||
if relation.field.rel.edit_inline == models.TABULAR:
|
if relation.field.rel.edit_inline == models.TABULAR:
|
||||||
bound_related_object_class = TabularBoundRelatedObject
|
bound_related_object_class = TabularBoundRelatedObject
|
||||||
|
|
|
@ -19,6 +19,8 @@ class CommentFormNode(template.Node):
|
||||||
ratings_optional=False, ratings_required=False, rating_options='',
|
ratings_optional=False, ratings_required=False, rating_options='',
|
||||||
is_public=True):
|
is_public=True):
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
|
if obj_id_lookup_var is not None:
|
||||||
|
obj_id_lookup_var = template.Variable(obj_id_lookup_var)
|
||||||
self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free
|
self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free
|
||||||
self.photos_optional, self.photos_required = photos_optional, photos_required
|
self.photos_optional, self.photos_required = photos_optional, photos_required
|
||||||
self.ratings_optional, self.ratings_required = ratings_optional, ratings_required
|
self.ratings_optional, self.ratings_required = ratings_optional, ratings_required
|
||||||
|
@ -32,7 +34,7 @@ class CommentFormNode(template.Node):
|
||||||
context.push()
|
context.push()
|
||||||
if self.obj_id_lookup_var is not None:
|
if self.obj_id_lookup_var is not None:
|
||||||
try:
|
try:
|
||||||
self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context)
|
self.obj_id = self.obj_id_lookup_var.resolve(context)
|
||||||
except template.VariableDoesNotExist:
|
except template.VariableDoesNotExist:
|
||||||
return ''
|
return ''
|
||||||
# Validate that this object ID is valid for this content-type.
|
# Validate that this object ID is valid for this content-type.
|
||||||
|
@ -75,6 +77,8 @@ class CommentFormNode(template.Node):
|
||||||
class CommentCountNode(template.Node):
|
class CommentCountNode(template.Node):
|
||||||
def __init__(self, package, module, context_var_name, obj_id, var_name, free):
|
def __init__(self, package, module, context_var_name, obj_id, var_name, free):
|
||||||
self.package, self.module = package, module
|
self.package, self.module = package, module
|
||||||
|
if context_var_name is not None:
|
||||||
|
context_var_name = template.Variable(context_var_name)
|
||||||
self.context_var_name, self.obj_id = context_var_name, obj_id
|
self.context_var_name, self.obj_id = context_var_name, obj_id
|
||||||
self.var_name, self.free = var_name, free
|
self.var_name, self.free = var_name, free
|
||||||
|
|
||||||
|
@ -82,7 +86,7 @@ class CommentCountNode(template.Node):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
manager = self.free and FreeComment.objects or Comment.objects
|
manager = self.free and FreeComment.objects or Comment.objects
|
||||||
if self.context_var_name is not None:
|
if self.context_var_name is not None:
|
||||||
self.obj_id = template.resolve_variable(self.context_var_name, context)
|
self.obj_id = self.context_var_name.resolve(context)
|
||||||
comment_count = manager.filter(object_id__exact=self.obj_id,
|
comment_count = manager.filter(object_id__exact=self.obj_id,
|
||||||
content_type__app_label__exact=self.package,
|
content_type__app_label__exact=self.package,
|
||||||
content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count()
|
content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count()
|
||||||
|
@ -92,6 +96,8 @@ class CommentCountNode(template.Node):
|
||||||
class CommentListNode(template.Node):
|
class CommentListNode(template.Node):
|
||||||
def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
|
def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
|
||||||
self.package, self.module = package, module
|
self.package, self.module = package, module
|
||||||
|
if context_var_name is not None:
|
||||||
|
context_var_name = template.Variable(context_var_name)
|
||||||
self.context_var_name, self.obj_id = context_var_name, obj_id
|
self.context_var_name, self.obj_id = context_var_name, obj_id
|
||||||
self.var_name, self.free = var_name, free
|
self.var_name, self.free = var_name, free
|
||||||
self.ordering = ordering
|
self.ordering = ordering
|
||||||
|
@ -102,7 +108,7 @@ class CommentListNode(template.Node):
|
||||||
get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma
|
get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma
|
||||||
if self.context_var_name is not None:
|
if self.context_var_name is not None:
|
||||||
try:
|
try:
|
||||||
self.obj_id = template.resolve_variable(self.context_var_name, context)
|
self.obj_id = self.context_var_name.resolve(context)
|
||||||
except template.VariableDoesNotExist:
|
except template.VariableDoesNotExist:
|
||||||
return ''
|
return ''
|
||||||
kwargs = {
|
kwargs = {
|
||||||
|
|
|
@ -88,8 +88,6 @@ UNKNOWN_SOURCE="<unknown source>"
|
||||||
tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
|
tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
|
||||||
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
|
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
|
||||||
re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
|
re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
|
||||||
# matches if the string is valid number
|
|
||||||
number_re = re.compile(r'[-+]?(\d+|\d*\.\d+)$')
|
|
||||||
|
|
||||||
# global dictionary of libraries that have been loaded using get_library
|
# global dictionary of libraries that have been loaded using get_library
|
||||||
libraries = {}
|
libraries = {}
|
||||||
|
@ -564,18 +562,19 @@ class FilterExpression(object):
|
||||||
elif constant_arg is not None:
|
elif constant_arg is not None:
|
||||||
args.append((False, constant_arg.replace(r'\"', '"')))
|
args.append((False, constant_arg.replace(r'\"', '"')))
|
||||||
elif var_arg:
|
elif var_arg:
|
||||||
args.append((True, var_arg))
|
args.append((True, Variable(var_arg)))
|
||||||
filter_func = parser.find_filter(filter_name)
|
filter_func = parser.find_filter(filter_name)
|
||||||
self.args_check(filter_name,filter_func, args)
|
self.args_check(filter_name,filter_func, args)
|
||||||
filters.append( (filter_func,args))
|
filters.append( (filter_func,args))
|
||||||
upto = match.end()
|
upto = match.end()
|
||||||
if upto != len(token):
|
if upto != len(token):
|
||||||
raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)
|
raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)
|
||||||
self.var, self.filters = var, filters
|
self.filters = filters
|
||||||
|
self.var = Variable(var)
|
||||||
|
|
||||||
def resolve(self, context, ignore_failures=False):
|
def resolve(self, context, ignore_failures=False):
|
||||||
try:
|
try:
|
||||||
obj = resolve_variable(self.var, context)
|
obj = self.var.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
if ignore_failures:
|
if ignore_failures:
|
||||||
obj = None
|
obj = None
|
||||||
|
@ -595,7 +594,7 @@ class FilterExpression(object):
|
||||||
if not lookup:
|
if not lookup:
|
||||||
arg_vals.append(arg)
|
arg_vals.append(arg)
|
||||||
else:
|
else:
|
||||||
arg_vals.append(resolve_variable(arg, context))
|
arg_vals.append(arg.resolve(context))
|
||||||
obj = func(obj, *arg_vals)
|
obj = func(obj, *arg_vals)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -637,37 +636,98 @@ class FilterExpression(object):
|
||||||
def resolve_variable(path, context):
|
def resolve_variable(path, context):
|
||||||
"""
|
"""
|
||||||
Returns the resolved variable, which may contain attribute syntax, within
|
Returns the resolved variable, which may contain attribute syntax, within
|
||||||
the given context. The variable may be a hard-coded string (if it begins
|
the given context.
|
||||||
and ends with single or double quote marks).
|
|
||||||
|
|
||||||
>>> c = {'article': {'section':'News'}}
|
Deprecated; use the Variable class instead.
|
||||||
>>> resolve_variable('article.section', c)
|
"""
|
||||||
u'News'
|
return Variable(path).resolve(context)
|
||||||
>>> resolve_variable('article', c)
|
|
||||||
{'section': 'News'}
|
class Variable(object):
|
||||||
>>> class AClass: pass
|
"""
|
||||||
>>> c = AClass()
|
A template variable, resolvable against a given context. The variable may be
|
||||||
>>> c.article = AClass()
|
a hard-coded string (if it begins and ends with single or double quote
|
||||||
>>> c.article.section = 'News'
|
marks)::
|
||||||
>>> resolve_variable('article.section', c)
|
|
||||||
u'News'
|
>>> c = {'article': {'section':'News'}}
|
||||||
|
>>> Variable('article.section').resolve(c)
|
||||||
|
u'News'
|
||||||
|
>>> Variable('article').resolve(c)
|
||||||
|
{'section': 'News'}
|
||||||
|
>>> class AClass: pass
|
||||||
|
>>> c = AClass()
|
||||||
|
>>> c.article = AClass()
|
||||||
|
>>> c.article.section = 'News'
|
||||||
|
>>> Variable('article.section').resolve(c)
|
||||||
|
u'News'
|
||||||
|
|
||||||
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
|
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
|
||||||
"""
|
"""
|
||||||
if number_re.match(path):
|
|
||||||
number_type = '.' in path and float or int
|
def __init__(self, var):
|
||||||
current = number_type(path)
|
self.var = var
|
||||||
elif path[0] in ('"', "'") and path[0] == path[-1]:
|
self.literal = None
|
||||||
current = path[1:-1]
|
self.lookups = None
|
||||||
else:
|
|
||||||
|
try:
|
||||||
|
# First try to treat this variable as a number.
|
||||||
|
#
|
||||||
|
# Note that this could cause an OverflowError here that we're not
|
||||||
|
# catching. Since this should only happen at compile time, that's
|
||||||
|
# probably OK.
|
||||||
|
self.literal = float(var)
|
||||||
|
|
||||||
|
# So it's a float... is it an int? If the original value contained a
|
||||||
|
# dot or an "e" then it was a float, not an int.
|
||||||
|
if '.' not in var and 'e' not in var.lower():
|
||||||
|
self.literal = int(self.literal)
|
||||||
|
|
||||||
|
# "2." is invalid
|
||||||
|
if var.endswith('.'):
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
# A ValueError means that the variable isn't a number.
|
||||||
|
# If it's wrapped with quotes (single or double), then
|
||||||
|
# we're also dealing with a literal.
|
||||||
|
if var[0] in "\"'" and var[0] == var[-1]:
|
||||||
|
self.literal = var[1:-1]
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Otherwise we'll set self.lookups so that resolve() knows we're
|
||||||
|
# dealing with a bonafide variable
|
||||||
|
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
|
||||||
|
|
||||||
|
def resolve(self, context):
|
||||||
|
"""Resolve this variable against a given context."""
|
||||||
|
if self.lookups is not None:
|
||||||
|
# We're dealing with a variable that needs to be resolved
|
||||||
|
return self._resolve_lookup(context)
|
||||||
|
else:
|
||||||
|
# We're dealing with a literal, so it's already been "resolved"
|
||||||
|
return self.literal
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s: %r>" % (self.__class__.__name__, self.var)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.var
|
||||||
|
|
||||||
|
def _resolve_lookup(self, context):
|
||||||
|
"""
|
||||||
|
Performs resolution of a real variable (i.e. not a literal) against the
|
||||||
|
given context.
|
||||||
|
|
||||||
|
As indicated by the method's name, this method is an implementation
|
||||||
|
detail and shouldn't be called by external code. Use Variable.resolve()
|
||||||
|
instead.
|
||||||
|
"""
|
||||||
current = context
|
current = context
|
||||||
bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
|
for bit in self.lookups:
|
||||||
while bits:
|
|
||||||
try: # dictionary lookup
|
try: # dictionary lookup
|
||||||
current = current[bits[0]]
|
current = current[bit]
|
||||||
except (TypeError, AttributeError, KeyError):
|
except (TypeError, AttributeError, KeyError):
|
||||||
try: # attribute lookup
|
try: # attribute lookup
|
||||||
current = getattr(current, bits[0])
|
current = getattr(current, bit)
|
||||||
if callable(current):
|
if callable(current):
|
||||||
if getattr(current, 'alters_data', False):
|
if getattr(current, 'alters_data', False):
|
||||||
current = settings.TEMPLATE_STRING_IF_INVALID
|
current = settings.TEMPLATE_STRING_IF_INVALID
|
||||||
|
@ -685,27 +745,27 @@ def resolve_variable(path, context):
|
||||||
raise
|
raise
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
try: # list-index lookup
|
try: # list-index lookup
|
||||||
current = current[int(bits[0])]
|
current = current[int(bit)]
|
||||||
except (IndexError, # list index out of range
|
except (IndexError, # list index out of range
|
||||||
ValueError, # invalid literal for int()
|
ValueError, # invalid literal for int()
|
||||||
KeyError, # current is a dict without `int(bits[0])` key
|
KeyError, # current is a dict without `int(bit)` key
|
||||||
TypeError, # unsubscriptable object
|
TypeError, # unsubscriptable object
|
||||||
):
|
):
|
||||||
raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute
|
raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if getattr(e, 'silent_variable_failure', False):
|
if getattr(e, 'silent_variable_failure', False):
|
||||||
current = settings.TEMPLATE_STRING_IF_INVALID
|
current = settings.TEMPLATE_STRING_IF_INVALID
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
del bits[0]
|
|
||||||
if isinstance(current, (basestring, Promise)):
|
if isinstance(current, (basestring, Promise)):
|
||||||
try:
|
try:
|
||||||
current = force_unicode(current)
|
current = force_unicode(current)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# Failing to convert to unicode can happen sometimes (e.g. debug
|
# Failing to convert to unicode can happen sometimes (e.g. debug
|
||||||
# tracebacks). So we allow it in this particular instance.
|
# tracebacks). So we allow it in this particular instance.
|
||||||
pass
|
pass
|
||||||
return current
|
return current
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
|
@ -861,10 +921,10 @@ class Library(object):
|
||||||
|
|
||||||
class SimpleNode(Node):
|
class SimpleNode(Node):
|
||||||
def __init__(self, vars_to_resolve):
|
def __init__(self, vars_to_resolve):
|
||||||
self.vars_to_resolve = vars_to_resolve
|
self.vars_to_resolve = map(Variable, vars_to_resolve)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
|
||||||
return func(*resolved_vars)
|
return func(*resolved_vars)
|
||||||
|
|
||||||
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
|
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode)
|
||||||
|
@ -883,10 +943,10 @@ class Library(object):
|
||||||
|
|
||||||
class InclusionNode(Node):
|
class InclusionNode(Node):
|
||||||
def __init__(self, vars_to_resolve):
|
def __init__(self, vars_to_resolve):
|
||||||
self.vars_to_resolve = vars_to_resolve
|
self.vars_to_resolve = map(Variable, vars_to_resolve)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
|
||||||
if takes_context:
|
if takes_context:
|
||||||
args = [context] + resolved_vars
|
args = [context] + resolved_vars
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"Default variable filters"
|
"Default variable filters"
|
||||||
|
|
||||||
from django.template import resolve_variable, Library
|
from django.template import Variable, Library
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext, ungettext
|
from django.utils.translation import ugettext, ungettext
|
||||||
from django.utils.encoding import force_unicode, smart_str, iri_to_uri
|
from django.utils.encoding import force_unicode, smart_str, iri_to_uri
|
||||||
|
@ -297,7 +297,8 @@ def dictsort(value, arg):
|
||||||
Takes a list of dicts, returns that list sorted by the property given in
|
Takes a list of dicts, returns that list sorted by the property given in
|
||||||
the argument.
|
the argument.
|
||||||
"""
|
"""
|
||||||
decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value]
|
var_resolve = Variable(arg).resolve
|
||||||
|
decorated = [(var_resolve(item), item) for item in value]
|
||||||
decorated.sort()
|
decorated.sort()
|
||||||
return [item[1] for item in decorated]
|
return [item[1] for item in decorated]
|
||||||
|
|
||||||
|
@ -306,7 +307,8 @@ def dictsortreversed(value, arg):
|
||||||
Takes a list of dicts, returns that list sorted in reverse order by the
|
Takes a list of dicts, returns that list sorted in reverse order by the
|
||||||
property given in the argument.
|
property given in the argument.
|
||||||
"""
|
"""
|
||||||
decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value]
|
var_resolve = Variable(arg).resolve
|
||||||
|
decorated = [(var_resolve(item), item) for item in value]
|
||||||
decorated.sort()
|
decorated.sort()
|
||||||
decorated.reverse()
|
decorated.reverse()
|
||||||
return [item[1] for item in decorated]
|
return [item[1] for item in decorated]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"Default tags used by the template system, available to all templates."
|
"Default tags used by the template system, available to all templates."
|
||||||
|
|
||||||
from django.template import Node, NodeList, Template, Context, resolve_variable
|
from django.template import Node, NodeList, Template, Context, Variable
|
||||||
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
|
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
|
||||||
from django.template import get_library, Library, InvalidTemplateLibrary
|
from django.template import get_library, Library, InvalidTemplateLibrary
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -30,7 +30,7 @@ class CycleNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
self.counter += 1
|
self.counter += 1
|
||||||
value = self.cyclevars[self.counter % self.cyclevars_len]
|
value = self.cyclevars[self.counter % self.cyclevars_len]
|
||||||
value = resolve_variable(value, context)
|
value = Variable(value).resolve(context)
|
||||||
if self.variable_name:
|
if self.variable_name:
|
||||||
context[self.variable_name] = value
|
context[self.variable_name] = value
|
||||||
return value
|
return value
|
||||||
|
@ -57,12 +57,12 @@ class FilterNode(Node):
|
||||||
|
|
||||||
class FirstOfNode(Node):
|
class FirstOfNode(Node):
|
||||||
def __init__(self, vars):
|
def __init__(self, vars):
|
||||||
self.vars = vars
|
self.vars = map(Variable, vars)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
for var in self.vars:
|
for var in self.vars:
|
||||||
try:
|
try:
|
||||||
value = resolve_variable(var, context)
|
value = var.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
continue
|
continue
|
||||||
if value:
|
if value:
|
||||||
|
@ -147,7 +147,7 @@ class IfChangedNode(Node):
|
||||||
def __init__(self, nodelist, *varlist):
|
def __init__(self, nodelist, *varlist):
|
||||||
self.nodelist = nodelist
|
self.nodelist = nodelist
|
||||||
self._last_seen = None
|
self._last_seen = None
|
||||||
self._varlist = varlist
|
self._varlist = map(Variable, varlist)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
if 'forloop' in context and context['forloop']['first']:
|
if 'forloop' in context and context['forloop']['first']:
|
||||||
|
@ -156,7 +156,7 @@ class IfChangedNode(Node):
|
||||||
if self._varlist:
|
if self._varlist:
|
||||||
# Consider multiple parameters.
|
# Consider multiple parameters.
|
||||||
# This automatically behaves like a OR evaluation of the multiple variables.
|
# This automatically behaves like a OR evaluation of the multiple variables.
|
||||||
compare_to = [resolve_variable(var, context) for var in self._varlist]
|
compare_to = [var.resolve(context) for var in self._varlist]
|
||||||
else:
|
else:
|
||||||
compare_to = self.nodelist.render(context)
|
compare_to = self.nodelist.render(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
|
@ -175,7 +175,7 @@ class IfChangedNode(Node):
|
||||||
|
|
||||||
class IfEqualNode(Node):
|
class IfEqualNode(Node):
|
||||||
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
|
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
|
||||||
self.var1, self.var2 = var1, var2
|
self.var1, self.var2 = Variable(var1), Variable(var2)
|
||||||
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
|
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
|
||||||
self.negate = negate
|
self.negate = negate
|
||||||
|
|
||||||
|
@ -184,11 +184,11 @@ class IfEqualNode(Node):
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
val1 = resolve_variable(self.var1, context)
|
val1 = self.var1.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
val1 = None
|
val1 = None
|
||||||
try:
|
try:
|
||||||
val2 = resolve_variable(self.var2, context)
|
val2 = self.var2.resolve(context)
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
val2 = None
|
val2 = None
|
||||||
if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
|
if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
|
from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
|
||||||
from django.template import Library, Node
|
from django.template import Library, Node
|
||||||
from django.template.loader import get_template, get_template_from_string, find_template_source
|
from django.template.loader import get_template, get_template_from_string, find_template_source
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -99,11 +99,11 @@ class ConstantIncludeNode(Node):
|
||||||
|
|
||||||
class IncludeNode(Node):
|
class IncludeNode(Node):
|
||||||
def __init__(self, template_name):
|
def __init__(self, template_name):
|
||||||
self.template_name = template_name
|
self.template_name = Variable(template_name)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
template_name = resolve_variable(self.template_name, context)
|
template_name = self.template_name.resolve(context)
|
||||||
t = get_template(template_name)
|
t = get_template(template_name)
|
||||||
return t.render(context)
|
return t.render(context)
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError, e:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.template import Node, resolve_variable
|
from django.template import Node, Variable
|
||||||
from django.template import TemplateSyntaxError, TokenParser, Library
|
from django.template import TemplateSyntaxError, TokenParser, Library
|
||||||
from django.template import TOKEN_TEXT, TOKEN_VAR
|
from django.template import TOKEN_TEXT, TOKEN_VAR
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
@ -32,11 +32,11 @@ class GetCurrentLanguageBidiNode(Node):
|
||||||
|
|
||||||
class TranslateNode(Node):
|
class TranslateNode(Node):
|
||||||
def __init__(self, value, noop):
|
def __init__(self, value, noop):
|
||||||
self.value = value
|
self.value = Variable(value)
|
||||||
self.noop = noop
|
self.noop = noop
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
value = resolve_variable(self.value, context)
|
value = self.value.resolve(context)
|
||||||
if self.noop:
|
if self.noop:
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -928,10 +928,36 @@ current context, available in the ``render`` method::
|
||||||
``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then
|
``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then
|
||||||
format it accordingly.
|
format it accordingly.
|
||||||
|
|
||||||
.. note::
|
.. admonition:: New in development version:
|
||||||
The ``resolve_variable()`` function will throw a ``VariableDoesNotExist``
|
|
||||||
exception if it cannot resolve the string passed to it in the current
|
Variable resolution has changed in the development version of Django.
|
||||||
context of the page.
|
``template.resolve_variable()`` is still available, but has been deprecated
|
||||||
|
in favor of a new ``template.Variable`` class. Using this class will usually
|
||||||
|
be more efficient than calling ``template.resolve_variable``
|
||||||
|
|
||||||
|
To use the ``Variable`` class, simply instantiate it with the name of the
|
||||||
|
variable to be resolved, and then call ``variable.resolve(context)``. So,
|
||||||
|
in the development version, the above example would be more correctly
|
||||||
|
written as:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
class FormatTimeNode(template.Node):
|
||||||
|
def __init__(self, date_to_be_formatted, format_string):
|
||||||
|
self.date_to_be_formatted = **Variable(date_to_be_formatted)**
|
||||||
|
self.format_string = format_string
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
try:
|
||||||
|
actual_date = **self.date_to_be_formatted.resolve(context)**
|
||||||
|
return actual_date.strftime(self.format_string)
|
||||||
|
except template.VariableDoesNotExist:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
Changes are highlighted in bold.
|
||||||
|
|
||||||
|
Variable resolution will throw a ``VariableDoesNotExist`` exception if it cannot
|
||||||
|
resolve the string passed to it in the current context of the page.
|
||||||
|
|
||||||
Shortcut for simple tags
|
Shortcut for simple tags
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
Loading…
Reference in New Issue