Commit 7db24dd2 authored by Malcolm Tredinnick's avatar Malcolm Tredinnick
Browse files

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
parent a6f429e3
Loading
Loading
Loading
Loading
+25 −30
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ class CommentNode(Node):

class CycleNode(Node):
    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

    def render(self, context):
@@ -70,14 +70,11 @@ class FilterNode(Node):

class FirstOfNode(Node):
    def __init__(self, vars):
        self.vars = map(Variable, vars)
        self.vars = vars

    def render(self, context):
        for var in self.vars:
            try:
            value = var.resolve(context)
            except VariableDoesNotExist:
                continue
            if value:
                return smart_unicode(value)
        return u''
@@ -169,7 +166,7 @@ class IfChangedNode(Node):
    def __init__(self, nodelist_true, nodelist_false, *varlist):
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
        self._last_seen = None
        self._varlist = map(Variable, varlist)
        self._varlist = varlist
        self._id = str(id(self))

    def render(self, context):
@@ -180,7 +177,7 @@ class IfChangedNode(Node):
            if self._varlist:
                # Consider multiple parameters.  This automatically behaves
                # 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:
                compare_to = self.nodelist_true.render(context)
        except VariableDoesNotExist:
@@ -200,7 +197,7 @@ class IfChangedNode(Node):

class IfEqualNode(Node):
    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.negate = negate

@@ -208,14 +205,8 @@ class IfEqualNode(Node):
        return "<IfEqualNode>"

    def render(self, context):
        try:
            val1 = self.var1.resolve(context)
        except VariableDoesNotExist:
            val1 = None
        try:
            val2 = self.var2.resolve(context)
        except VariableDoesNotExist:
            val2 = None
        val1 = self.var1.resolve(context, True)
        val2 = self.var2.resolve(context, True)
        if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
            return self.nodelist_true.render(context)
        return self.nodelist_false.render(context)
@@ -372,7 +363,6 @@ class URLNode(Node):
        kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
                       for k, v in self.kwargs.items()])

        
        # 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,
        # re-raise the NoReverseMatch unless we're using the
@@ -514,12 +504,14 @@ def cycle(parser, token):

    if len(args) > 4 and args[-2] == 'as':
        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'):
            parser._namedCycleNodes = {}
        parser._namedCycleNodes[name] = node
    else:
        node = CycleNode(args[1:])
        values = [parser.compile_filter(arg) for arg in args[1:]]
        node = CycleNode(values)
    return node
cycle = register.tag(cycle)

@@ -594,7 +586,7 @@ def firstof(parser, token):
    if len(bits) < 1:
        raise TemplateSyntaxError("'firstof' statement requires at least one"
                                  " argument")
    return FirstOfNode(bits)
    return FirstOfNode([parser.compile_filter(bit) for bit in bits])
firstof = register.tag(firstof)

#@register.tag(name="for")
@@ -701,7 +693,9 @@ def do_ifequal(parser, token, negate):
        parser.delete_first_token()
    else:
        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
def ifequal(parser, token):
@@ -864,7 +858,8 @@ def ifchanged(parser, token):
        parser.delete_first_token()
    else:
        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)

#@register.tag
+8 −1
Original line number Diff line number Diff line
@@ -424,13 +424,13 @@ class Templates(unittest.TestCase):
            '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'),
            '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'),
            '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'),
            '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'),
            '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 ############################################################

@@ -638,6 +638,13 @@ class Templates(unittest.TestCase):
            'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, '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 ########################################################
            'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
            'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),