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
- ==, !=
- not ....
- v in (1,"y",z)
- <=, <, >=, >
- 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
- Template tag - list punctuation for a list of items by shapiromatron 8 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 3 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 4 months ago
- Help text hyperlinks by sa2812 1 year, 5 months ago
Comments
For the sake of clarity in my templates, I've replaced the two instances of
'endif'
with'end'+TAGNAME
. So for now it'sendpyif
instead ofendif
. Didn't want to confuse it with any regular{% endif %}
in my templates.#
Please login first before commenting.