Fixed #5756, #6296 -- Most template tags can now handle filters in arguments.

Most of the hard work for this was done by akaihola.

There are still possibly some problems with the i18n template tags,
since they are written quite differently and a few other bug fixes have
to made before they can be fixed.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10119 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2009-03-23 09:42:51 +00:00
parent a6f429e37e
commit 7db24dd2b0
2 changed files with 33 additions and 31 deletions

View File

@ -39,7 +39,7 @@ class CommentNode(Node):
class CycleNode(Node): class CycleNode(Node):
def __init__(self, cyclevars, variable_name=None): def __init__(self, cyclevars, variable_name=None):
self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars]) self.cycle_iter = itertools_cycle(cyclevars)
self.variable_name = variable_name self.variable_name = variable_name
def render(self, context): def render(self, context):
@ -70,14 +70,11 @@ class FilterNode(Node):
class FirstOfNode(Node): class FirstOfNode(Node):
def __init__(self, vars): def __init__(self, vars):
self.vars = map(Variable, vars) self.vars = vars
def render(self, context): def render(self, context):
for var in self.vars: for var in self.vars:
try: value = var.resolve(context)
value = var.resolve(context)
except VariableDoesNotExist:
continue
if value: if value:
return smart_unicode(value) return smart_unicode(value)
return u'' return u''
@ -169,7 +166,7 @@ class IfChangedNode(Node):
def __init__(self, nodelist_true, nodelist_false, *varlist): def __init__(self, nodelist_true, nodelist_false, *varlist):
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
self._last_seen = None self._last_seen = None
self._varlist = map(Variable, varlist) self._varlist = varlist
self._id = str(id(self)) self._id = str(id(self))
def render(self, context): def render(self, context):
@ -180,7 +177,7 @@ class IfChangedNode(Node):
if self._varlist: if self._varlist:
# Consider multiple parameters. This automatically behaves # Consider multiple parameters. This automatically behaves
# like an OR evaluation of the multiple variables. # like an OR evaluation of the multiple variables.
compare_to = [var.resolve(context) for var in self._varlist] compare_to = [var.resolve(context, True) for var in self._varlist]
else: else:
compare_to = self.nodelist_true.render(context) compare_to = self.nodelist_true.render(context)
except VariableDoesNotExist: except VariableDoesNotExist:
@ -200,7 +197,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 = Variable(var1), Variable(var2) self.var1, self.var2 = var1, 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
@ -208,14 +205,8 @@ class IfEqualNode(Node):
return "<IfEqualNode>" return "<IfEqualNode>"
def render(self, context): def render(self, context):
try: val1 = self.var1.resolve(context, True)
val1 = self.var1.resolve(context) val2 = self.var2.resolve(context, True)
except VariableDoesNotExist:
val1 = None
try:
val2 = self.var2.resolve(context)
except VariableDoesNotExist:
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):
return self.nodelist_true.render(context) return self.nodelist_true.render(context)
return self.nodelist_false.render(context) return self.nodelist_false.render(context)
@ -371,11 +362,10 @@ class URLNode(Node):
args = [arg.resolve(context) for arg in self.args] args = [arg.resolve(context) for arg in self.args]
kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
for k, v in self.kwargs.items()]) for k, v in self.kwargs.items()])
# Try to look up the URL twice: once given the view name, and again # Try to look up the URL twice: once given the view name, and again
# relative to what we guess is the "main" app. If they both fail, # relative to what we guess is the "main" app. If they both fail,
# re-raise the NoReverseMatch unless we're using the # re-raise the NoReverseMatch unless we're using the
# {% url ... as var %} construct in which cause return nothing. # {% url ... as var %} construct in which cause return nothing.
url = '' url = ''
try: try:
@ -388,7 +378,7 @@ class URLNode(Node):
except NoReverseMatch: except NoReverseMatch:
if self.asvar is None: if self.asvar is None:
raise raise
if self.asvar: if self.asvar:
context[self.asvar] = url context[self.asvar] = url
return '' return ''
@ -514,12 +504,14 @@ def cycle(parser, token):
if len(args) > 4 and args[-2] == 'as': if len(args) > 4 and args[-2] == 'as':
name = args[-1] name = args[-1]
node = CycleNode(args[1:-2], name) values = [parser.compile_filter(arg) for arg in args[1:-2]]
node = CycleNode(values, name)
if not hasattr(parser, '_namedCycleNodes'): if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {} parser._namedCycleNodes = {}
parser._namedCycleNodes[name] = node parser._namedCycleNodes[name] = node
else: else:
node = CycleNode(args[1:]) values = [parser.compile_filter(arg) for arg in args[1:]]
node = CycleNode(values)
return node return node
cycle = register.tag(cycle) cycle = register.tag(cycle)
@ -594,7 +586,7 @@ def firstof(parser, token):
if len(bits) < 1: if len(bits) < 1:
raise TemplateSyntaxError("'firstof' statement requires at least one" raise TemplateSyntaxError("'firstof' statement requires at least one"
" argument") " argument")
return FirstOfNode(bits) return FirstOfNode([parser.compile_filter(bit) for bit in bits])
firstof = register.tag(firstof) firstof = register.tag(firstof)
#@register.tag(name="for") #@register.tag(name="for")
@ -629,10 +621,10 @@ def do_for(parser, token):
<li>Sorry, no athletes in this list.</li> <li>Sorry, no athletes in this list.</li>
{% endfor %} {% endfor %}
<ul> <ul>
The above is equivalent to -- but shorter, cleaner, and possibly faster The above is equivalent to -- but shorter, cleaner, and possibly faster
than -- the following:: than -- the following::
<ul> <ul>
{% if althete_list %} {% if althete_list %}
{% for athlete in athlete_list %} {% for athlete in athlete_list %}
@ -701,7 +693,9 @@ def do_ifequal(parser, token, negate):
parser.delete_first_token() parser.delete_first_token()
else: else:
nodelist_false = NodeList() nodelist_false = NodeList()
return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate) val1 = parser.compile_filter(bits[1])
val2 = parser.compile_filter(bits[2])
return IfEqualNode(val1, val2, nodelist_true, nodelist_false, negate)
#@register.tag #@register.tag
def ifequal(parser, token): def ifequal(parser, token):
@ -864,7 +858,8 @@ def ifchanged(parser, token):
parser.delete_first_token() parser.delete_first_token()
else: else:
nodelist_false = NodeList() nodelist_false = NodeList()
return IfChangedNode(nodelist_true, nodelist_false, *bits[1:]) values = [parser.compile_filter(bit) for bit in bits[1:]]
return IfChangedNode(nodelist_true, nodelist_false, *values)
ifchanged = register.tag(ifchanged) ifchanged = register.tag(ifchanged)
#@register.tag #@register.tag
@ -1105,7 +1100,7 @@ def url(parser, token):
args = [] args = []
kwargs = {} kwargs = {}
asvar = None asvar = None
if len(bits) > 2: if len(bits) > 2:
bits = iter(bits[2:]) bits = iter(bits[2:])
for bit in bits: for bit in bits:

View File

@ -424,13 +424,13 @@ class Templates(unittest.TestCase):
'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError), 'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'), 'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'), 'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
# New format:
'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'), 'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'),
'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'), 'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'),
'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'), 'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'),
'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'), 'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'), 'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'),
'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'), 'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'),
'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A','two': '2'}, 'a2'),
### EXCEPTIONS ############################################################ ### EXCEPTIONS ############################################################
@ -638,6 +638,13 @@ class Templates(unittest.TestCase):
'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'), 'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'),
'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'), 'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
# FILTER EXPRESSIONS AS ARGUMENTS
'ifequal-filter01': ('{% ifequal a|upper "A" %}x{% endifequal %}', {'a': 'a'}, 'x'),
'ifequal-filter02': ('{% ifequal "A" a|upper %}x{% endifequal %}', {'a': 'a'}, 'x'),
'ifequal-filter03': ('{% ifequal a|upper b|upper %}x{% endifequal %}', {'a': 'x', 'b': 'X'}, 'x'),
'ifequal-filter04': ('{% ifequal x|slice:"1" "a" %}x{% endifequal %}', {'x': 'aaa'}, 'x'),
'ifequal-filter05': ('{% ifequal x|slice:"1"|upper "A" %}x{% endifequal %}', {'x': 'aaa'}, 'x'),
### IFNOTEQUAL TAG ######################################################## ### IFNOTEQUAL TAG ########################################################
'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"), 'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""), 'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),