Django's templates don't provide much in the way of arithmetic: there is an "add" filter and that is about it. Even if sub, mult and div filters are implemented, it is difficult to chain filters while preserving some complicated expression, such as ((x+3)4-(2-y)/12.75). However, this expression can be converted into Reverse Polish Notation: x 3 + 4 * 2 y - 12.75 / - which is just a sequence of operations (push-value or apply-operator) and can be chained.
To use these filters, first create a new stack for the expression with name|stnew (pass it some locally unique value). To push a number (or template variable) onto the stack, call name|stpush:number (note that you have to tell stpush the name of the stack to push onto). To pop, call name|stpop. To perform an operation, call name|st[add,sub,mult,div,mod]:number. All numbers are integers if they look like integers, or floats otherwise (integers are turned into floats upon division if they need to be). All of these functions return the name of the stack so that they can be chained. When the calculation is finished (i.e. the answer is at the bottom of the stack) call name|stget to retrieve it.
Example (this was used to calculate an inline CSS value:
left: {{ forloop.counter|stnew|stpush:res.stwkday|stpush:"9.35"|stmult|stpush:res.get_item_left|stpush:"2.75"|stadd|stadd|stget }}em;
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 97 98 | from django import template
register = template.Library()
class stack:
def __init__(self):
self.stack = []
def push(self, o):
self.stack.append(o)
#print 'push', self.stack
def pop(self):
if len(self.stack) == 0:
raise KeyError, "Stack is empty"
o = self.stack[-1]
#print 'pop', self.stack
del self.stack[-1]
return o
def is_empty(self):
return len(self.stack) == 0
def __len__(self):
return len(self.stack)
# truncate a floating point number only if it has no decimal part (convert from string if necessary)
def number(num):
f = float(num)
i = int(f)
if i == f: #FIXME: floating point equality?
return i
return f
stacks = {}
@register.filter
def stnew(value):
#print 'stnew'
stacks[value] = stack()
return value
@register.filter
def stpush(value, arg):
#print 'stpush:',
stacks[value].push(number(arg))
return value
@register.filter
def stpop(value):
#print 'stpop:',
if value in stacks:
stacks[value].pop()
return value
@register.filter
def stget(value):
#print 'stget:',
if value in stacks:
return stacks[value].pop()
@register.filter
def stadd(value):
#print 'stadd:',
two = stacks[value].pop()
one = stacks[value].pop()
stacks[value].push(one + two)
return value
@register.filter
def stsub(value):
#print 'stsub:',
two = stacks[value].pop()
one = stacks[value].pop()
stacks[value].push(one - two)
return value
@register.filter
def stmult(value):
#print 'stmult:',
two = stacks[value].pop()
one = stacks[value].pop()
stacks[value].push(one * two)
return value
@register.filter
def stdiv(value):
#print 'stdiv:',
two = stacks[value].pop()
one = stacks[value].pop()
stacks[value].push(number(float(one) / float(two)))
return value
@register.filter
def stmod(value):
two = stacks[value].pop()
one = stacks[value].pop()
stacks[value].push(one % two)
return value
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks 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, 6 months ago
Comments
Please login first before commenting.