Login

PyIf Template Tag (Conditional Tag)

Author:
nstrite
Posted:
March 23, 2007
Language:
Python
Version:
.96
Score:
12 (after 12 ratings)

Cheers to limodou for getting me thinking about this. The only problem with his implementation is that it doesn't support Django's "." syntax for accessing array/dict elements. In the Django style of allowing simple syntax for designers while allowing for greater flexibility, and less template duplication for conditionals that were previously impossible to represent in templates, I modified Django's built-in If tag.

This is an adaptation/enhancement to Django's built in IfNode {% if ... %} that combines if ifequal ifnotequal into one and then adds even more. This

Supports

  1. ==, !=
  2. not ....
  3. v in (1,"y",z)
  4. <=, <, >=, >
  5. nesting (True and (False or (True or False)))

How to use it:

{% pyif i == 1 or (5 >= i and i != 7) and user.first_name in ('John', 'Jacob') %} 'Tis true. {% else %} 'Tis false. {% endif %}

I hope you like it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from django.template import Node, NodeList
from django.template import TemplateSyntaxError, VariableDoesNotExist
from django.template import Library
import re

# For Python 2.3
if not hasattr(__builtins__, 'set'):
    from sets import Set as set

register = Library()
variable_re = re.compile(r'[\w._\|\"\']+')
string_re = re.compile(r'^([\"\']).*\1$')

TAGNAME = 'pyif' # Hopefully this can replace django's built-in if tag

class IfNode(Node):
    def __init__(self, expression, variables, nodelist_true, nodelist_false):
        self.expression = expression
        self.variables = variables
        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false

    def __repr__(self):
        return "<If node>"

    def __iter__(self):
        for node in self.nodelist_true:
            yield node
        for node in self.nodelist_false:
            yield node

    def get_nodes_by_type(self, nodetype):
        nodes = []
        if isinstance(self, nodetype):
            nodes.append(self)
        nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
        nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
        return nodes

    def render(self, context):
        variable_context = {}
        expression = self.expression
        for variable in self.variables:
            try:
                value = variable.resolve(context, True)
            except VariableDoesNotExist:
                value = None

            context_name = 'var%s' % len(variable_context)
            expression = re.sub(r'(?<![\w._\|])%s(?![\w._\|])' % re.escape(variable.token), context_name, expression)
            variable_context[context_name] = value
        try:
            resultant = eval(expression, variable_context)
        except:
            resultant = False

        if not resultant:
            return self.nodelist_false.render(context)
        return self.nodelist_true.render(context)

def do_if(parser, token):
    bits = token.contents.split(None, 1)
    if len(bits) != 2:
        raise TemplateSyntaxError, "'if' statement requires at least one argument"
    expression = bits[1]
    variables = set([ parser.compile_filter(x) for x in variable_re.findall(expression) if x not in ('and', 'or', 'not', 'in') and not string_re.match(x) ])
    nodelist_true = parser.parse(('else', 'endif'))
    token = parser.next_token()
    if token.contents == 'else':
        nodelist_false = parser.parse(('endif',))
        parser.delete_first_token()
    else:
        nodelist_false = NodeList()
    return IfNode(expression, variables, nodelist_true, nodelist_false)
do_if = register.tag(TAGNAME, do_if)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

gsf0 (on September 21, 2007):

For the sake of clarity in my templates, I've replaced the two instances of 'endif' with 'end'+TAGNAME. So for now it's endpyif instead of endif. Didn't want to confuse it with any regular {% endif %} in my templates.

#

Please login first before commenting.