templatetags: manage decimals on mathematics filters (#41868)
This commit is contained in:
parent
ca13ccb98d
commit
219344e0db
|
@ -18,6 +18,8 @@ from __future__ import absolute_import
|
|||
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from decimal import InvalidOperation as DecimalInvalidOperation
|
||||
from decimal import DivisionByZero as DecimalDivisionByZero
|
||||
import json
|
||||
import time
|
||||
|
||||
|
@ -441,20 +443,55 @@ def age_in_months(value, today=None):
|
|||
return ''
|
||||
return years * 12 + months
|
||||
|
||||
def parse_decimal(value, default=Decimal(0)):
|
||||
if isinstance(value, six.string_types):
|
||||
# replace , by . for French users comfort
|
||||
value = value.replace(',', '.')
|
||||
try:
|
||||
return Decimal(value).quantize(Decimal('1.0000')).normalize()
|
||||
except (ArithmeticError, TypeError):
|
||||
return default
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def decimal(value, arg=None):
|
||||
if not isinstance(value, Decimal):
|
||||
if isinstance(value, six.string_types):
|
||||
# replace , by . for French users comfort
|
||||
value = value.replace(',', '.')
|
||||
try:
|
||||
value = Decimal(value).quantize(Decimal('1.0000')).normalize()
|
||||
except (ArithmeticError, TypeError):
|
||||
value = Decimal(0)
|
||||
value = parse_decimal(value)
|
||||
if arg is None:
|
||||
return value
|
||||
return defaultfilters.floatformat(value, arg=arg)
|
||||
|
||||
@register.filter
|
||||
def add(term1, term2):
|
||||
'''replace the "add" native django filter'''
|
||||
|
||||
term1_decimal = parse_decimal(term1, default=None)
|
||||
term2_decimal = parse_decimal(term2, default=None)
|
||||
|
||||
if term1_decimal is not None and term2_decimal is not None:
|
||||
return term1_decimal + term2_decimal
|
||||
if not term1 and term2_decimal is not None:
|
||||
return term2_decimal
|
||||
if not term2 and term1_decimal is not None:
|
||||
return term1_decimal
|
||||
return defaultfilters.add(term1, term2)
|
||||
|
||||
@register.filter
|
||||
def subtract(term1, term2):
|
||||
return parse_decimal(term1) - parse_decimal(term2)
|
||||
|
||||
@register.filter
|
||||
def multiply(term1, term2):
|
||||
return parse_decimal(term1) * parse_decimal(term2)
|
||||
|
||||
@register.filter
|
||||
def divide(term1, term2):
|
||||
try:
|
||||
return parse_decimal(term1) / parse_decimal(term2)
|
||||
except DecimalInvalidOperation:
|
||||
return ''
|
||||
except DecimalDivisionByZero:
|
||||
return ''
|
||||
|
||||
|
||||
_json_script_escapes = {
|
||||
ord('>'): '\\u003E',
|
||||
|
|
|
@ -544,6 +544,86 @@ def test_decimal_templatetag():
|
|||
assert tmpl.render(Context()) == 'hello'
|
||||
|
||||
|
||||
def test_mathematics_templatetag():
|
||||
tmpl = Template('{{ term1|add:term2 }}')
|
||||
|
||||
# using strings
|
||||
assert tmpl.render(Context({'term1': '1.1', 'term2': 0})) == '1.1'
|
||||
assert tmpl.render(Context({'term1': 'not a number', 'term2': 1.2})) == ''
|
||||
assert tmpl.render(Context({'term1': 0.3, 'term2': "1"})) == '1.3'
|
||||
assert tmpl.render(Context({'term1': 1.4, 'term2': "not a number"})) == ''
|
||||
|
||||
# add
|
||||
assert tmpl.render(Context({'term1': 4, 'term2': -0.9})) == '3.1'
|
||||
assert tmpl.render(Context({'term1': '4', 'term2': -0.8})) == '3.2'
|
||||
assert tmpl.render(Context({'term1': 4, 'term2': '-0.7'})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': '4', 'term2': '-0.6'})) == '3.4'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': 3.5})) == '3.5'
|
||||
assert tmpl.render(Context({'term1': None, 'term2': 3.5})) == '3.5'
|
||||
assert tmpl.render(Context({'term1': 3.6, 'term2': ''})) == '3.6'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': ''})) == ''
|
||||
assert tmpl.render(Context({'term1': 3.6, 'term2': None})) == '3.6'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': ''})) == '0'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': 0})) == '0'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': 0})) == '0'
|
||||
|
||||
# add using ',' instead of '.' decimal separator
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': '2,2'})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': '2.2'})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': 2.2})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': 0})) == '1.1'
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': ''})) == '1.1'
|
||||
assert tmpl.render(Context({'term1': '1,1', 'term2': None})) == '1.1'
|
||||
assert tmpl.render(Context({'term1': '1.1', 'term2': '2,2'})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': 1.1, 'term2': '2,2'})) == '3.3'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': '2,2'})) == '2.2'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': '2,2'})) == '2.2'
|
||||
assert tmpl.render(Context({'term1': None, 'term2': '2,2'})) == '2.2'
|
||||
|
||||
# subtract
|
||||
tmpl = Template('{{ term1|subtract:term2 }}')
|
||||
assert tmpl.render(Context({'term1': 5.1, 'term2': 1})) == '4.1'
|
||||
assert tmpl.render(Context({'term1': '5.2', 'term2': 1})) == '4.2'
|
||||
assert tmpl.render(Context({'term1': 5.3, 'term2': '1'})) == '4.3'
|
||||
assert tmpl.render(Context({'term1': '5.4', 'term2': '1'})) == '4.4'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': -4.5})) == '4.5'
|
||||
assert tmpl.render(Context({'term1': 4.6, 'term2': ''})) == '4.6'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': ''})) == '0'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': ''})) == '0'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': 0})) == '0'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': 0})) == '0'
|
||||
|
||||
# multiply
|
||||
tmpl = Template('{{ term1|multiply:term2 }}')
|
||||
assert tmpl.render(Context({'term1': '3', 'term2': '2'})) == '6'
|
||||
assert tmpl.render(Context({'term1': 2.5, 'term2': 2})) == '5.0'
|
||||
assert tmpl.render(Context({'term1': '2.5', 'term2': 2})) == '5.0'
|
||||
assert tmpl.render(Context({'term1': 2.5, 'term2': '2'})) == '5.0'
|
||||
assert tmpl.render(Context({'term1': '2.5', 'term2': '2'})) == '5.0'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': '2'})) == '0'
|
||||
assert tmpl.render(Context({'term1': 2.5, 'term2': ''})) == '0.0'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': ''})) == '0'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': ''})) == '0'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': 0})) == '0'
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': 0})) == '0'
|
||||
|
||||
# divide
|
||||
tmpl = Template('{{ term1|divide:term2 }}')
|
||||
assert tmpl.render(Context({'term1': 16, 'term2': 2})) == '8'
|
||||
assert tmpl.render(Context({'term1': 6, 'term2': 0.75})) == '8'
|
||||
assert tmpl.render(Context({'term1': '6', 'term2': 0.75})) == '8'
|
||||
assert tmpl.render(Context({'term1': 6, 'term2': '0.75'})) == '8'
|
||||
assert tmpl.render(Context({'term1': '6', 'term2': '0.75'})) == '8'
|
||||
assert tmpl.render(Context({'term1': '', 'term2': '2'})) == '0'
|
||||
assert tmpl.render(Context({'term1': 6, 'term2': ''})) == ''
|
||||
assert tmpl.render(Context({'term1': '', 'term2': ''})) == ''
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': ''})) == ''
|
||||
assert tmpl.render(Context({'term1': '', 'term2': 0})) == ''
|
||||
assert tmpl.render(Context({'term1': 0, 'term2': 0})) == ''
|
||||
tmpl = Template('{{ term1|divide:term2|decimal:2 }}')
|
||||
assert tmpl.render(Context({'term1': 2, 'term2': 3})) == '0.67'
|
||||
|
||||
|
||||
def test_json_script():
|
||||
tmpl = Template('{{ plop|json_script:"toto" }}')
|
||||
assert tmpl.render(Context({'plop': {'a': 'b'}})) == '<script id="toto" type="application/json">{"a": "b"}</script>'
|
||||
|
|
Loading…
Reference in New Issue