- Author:
- fragsworth
- Posted:
- May 29, 2009
- Language:
- Python
- Version:
- 1.0
- Score:
- 5 (after 5 ratings)
This is a conditional templatetag decorator that makes it very easy to write template tags that can be used as conditions. This can help avoid template boilerplate code (e.g. setting a variable in your template to be used in a condition).
All you have to do is define a function with expected parameters that returns True or False. Examples are in the code.
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | from django import template
from django.template import NodeList
from functools import wraps
import inspect
def condition_tag(func):
""" Generic conditional templatetag decorator.
Example - how to define a conditional tag:
@register.tag
@condition_tag
def if_can_foo(object, user='user'):
return user.can_foo(object)
Example - how to use it in the template:
{% if_can_foo object user %}
...
{% else %}
...
{% endif_can_foo %}
Or - we can leave out the second parameter, and it will default
to 'user':
{% if_can_foo object %}
...
In python, the if_can_foo function's arguments are the expected
arguments to the template tag. In this case, the first argument
should be the object, the second argument should be the user. The
return value must be either True or False, corresponding to the
if/else sections of the condition node in the template.
Default arguments for the function (e.g. user='user') will be
processed by the context that the template tag resides in. In this
case, it will resolve the global 'user' name, and in the function,
we will be accessing the resultant object. If this value does not
exist in the template's context, it will break.
"""
class ConditionNode(template.Node):
def __init__(self, arg_expressions, nodelist_true, nodelist_false):
self.arg_expressions = arg_expressions
self.nodelist_true = nodelist_true
self.nodelist_false = nodelist_false
def render(self, context):
params = [ i.resolve(context) for i in self.arg_expressions ]
if func(*params):
return self.nodelist_true.render(context)
else:
return self.nodelist_false.render(context)
@wraps(func)
def wrapper(parser, token):
bits = token.contents.split()
# Get the parameters and default parameters for the decorated function
argspec = inspect.getargspec(func)
params = argspec[0]
defaults = list(argspec[3])
if defaults:
default_params = dict(zip([i for i in reversed(params)],
[i for i in reversed(defaults)] ))
else:
default_params = {}
# Try to display nice template errors
if len(bits) > len(params)+1 or len(bits) <= len(params)-len(defaults):
error = (
'"%s" accepts %d arguments: %s' %
(bits[0], len(params), ', '.join(params),)
)
raise template.TemplateSyntaxError, error
# Get the (string) arguments from the template
arg_expressions = []
for i in range(len(params)):
try:
# Try to use the parameter given by the template
arg_expressions.append(parser.compile_filter(bits[i+1]))
except IndexError:
# If it doesn't exist, use the default value
arg_expressions.append(
parser.compile_filter(default_params[params[i]])
)
# Parse out the true and false nodes
nodelist_true = parser.parse(('else', 'end'+bits[0],))
token = parser.next_token()
if token.contents == 'else':
nodelist_false = parser.parse(('end'+bits[0],))
parser.delete_first_token()
else:
nodelist_false = NodeList()
return ConditionNode(arg_expressions, nodelist_true, nodelist_false)
return wrapper
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
If no defaults arguments are given line 60 fails because it is None and not an empty list.
Changing line 60 to this will fix it.
Otherwise an excellent snippet.
#
Please login first before commenting.