templatetags: manage string concatenation on add filter (#42062)

This commit is contained in:
Nicolas Roche 2020-04-24 10:50:17 +02:00
parent 017b204bcd
commit 942afd8e9f
3 changed files with 160 additions and 6 deletions

View File

@ -1620,6 +1620,103 @@ def test_lazy_formdata_mathematics_filters(pub):
assert tmpl.render(context) == '0.75'
def test_lazy_formdata_add_filters(pub):
formdef = FormDef()
formdef.name = 'foobar'
formdef.url_name = 'foobar'
formdef.fields = [
fields.StringField(id='0', label='term0', varname='term0'),
fields.StringField(id='1', label='term1', varname='term1'),
fields.StringField(id='2', label='term2', varname='term2'),
fields.StringField(id='3', label='term3', varname='term3'),
fields.StringField(id='4', label='term4', varname='term4'),
]
formdef.store()
formdata = formdef.data_class()()
formdata.data = {'0': 'foo', '1': '3,14', '2': '', '3': None}
formdata.store()
pub.substitutions.feed(formdata)
for mode in (None, 'lazy'):
context = pub.substitutions.get_context_variables(mode=mode)
# add
tmpl = Template('{{ form_var_term1|add:form_var_term1 }}')
assert tmpl.render(context) == '6.28'
tmpl = Template('{{ form_var_term1|add:form_var_term2 }}')
assert tmpl.render(context) == '3.14'
tmpl = Template('{{ form_var_term1|add:form_var_term3 }}')
assert tmpl.render(context) == '3.14'
tmpl = Template('{{ form_var_term1|add:form_var_term4 }}')
assert tmpl.render(context) == '3.14'
tmpl = Template('{{ form_var_term2|add:form_var_term1 }}')
assert tmpl.render(context) == '3.14'
tmpl = Template('{{ form_var_term3|add:form_var_term1 }}')
assert tmpl.render(context) == '3.14'
tmpl = Template('{{ form_var_term4|add:form_var_term1 }}')
assert tmpl.render(context) == '3.14'
# fallback to Django native add filter
tmpl = Template('{{ form_var_term0|add:form_var_term0 }}')
assert tmpl.render(context) == 'foofoo'
tmpl = Template('{{ form_var_term0|add:form_var_term1 }}')
assert tmpl.render(context) == 'foo3,14'
tmpl = Template('{{ form_var_term0|add:form_var_term2 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term0|add:form_var_term3 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term0|add:form_var_term4 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term1|add:form_var_term0 }}')
assert tmpl.render(context) == '3,14foo'
tmpl = Template('{{ form_var_term2|add:form_var_term0 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term2|add:form_var_term2 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term2|add:form_var_term3 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term2|add:form_var_term4 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term3|add:form_var_term0 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term3|add:form_var_term2 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term3|add:form_var_term3 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term3|add:form_var_term4 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term4|add:form_var_term0 }}')
assert tmpl.render(context) == 'foo'
tmpl = Template('{{ form_var_term4|add:form_var_term2 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term4|add:form_var_term3 }}')
assert tmpl.render(context) == ''
tmpl = Template('{{ form_var_term4|add:form_var_term4 }}')
assert tmpl.render(context) == ''
def test_mathematic_conditions_django(pub, variable_test_data):
for true_condition_value in (
# reminder

View File

@ -364,11 +364,11 @@ def test_decimal_templatetag():
def test_mathematics_templatetag():
tmpl = Template('{{ term1|add:term2 }}')
# using string: convert to number or 0
# using strings
assert tmpl.render({'term1': '1.1', 'term2': 0}) == '1.1'
assert tmpl.render({'term1': 'not a number', 'term2': 1.2}) == '1.2'
assert tmpl.render({'term1': 'not a number', 'term2': 1.2}) == ''
assert tmpl.render({'term1': 0.3, 'term2': "1"}) == '1.3'
assert tmpl.render({'term1': 1.4, 'term2': "not a number"}) == '1.4'
assert tmpl.render({'term1': 1.4, 'term2': "not a number"}) == ''
# add
assert tmpl.render({'term1': 4, 'term2': -0.9}) == '3.1'
@ -376,12 +376,37 @@ def test_mathematics_templatetag():
assert tmpl.render({'term1': 4, 'term2': '-0.7'}) == '3.3'
assert tmpl.render({'term1': '4', 'term2': '-0.6'}) == '3.4'
assert tmpl.render({'term1': '', 'term2': 3.5}) == '3.5'
assert tmpl.render({'term1': None, 'term2': 3.5}) == '3.5'
assert tmpl.render({'term1': 3.6, 'term2': ''}) == '3.6'
assert tmpl.render({'term1': '', 'term2': ''}) == '0'
assert tmpl.render({'term1': '', 'term2': ''}) == ''
assert tmpl.render({'term1': 3.6, 'term2': None}) == '3.6'
assert tmpl.render({'term1': 0, 'term2': ''}) == '0'
assert tmpl.render({'term1': '', 'term2': 0}) == '0'
assert tmpl.render({'term1': 0, 'term2': 0}) == '0'
# add using ',' instead of '.' decimal separator
assert tmpl.render({'term1': '1,1', 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': '2.2'}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': 2.2}) == '3.3'
assert tmpl.render({'term1': '1,1', 'term2': 0}) == '1.1'
assert tmpl.render({'term1': '1,1', 'term2': ''}) == '1.1'
assert tmpl.render({'term1': '1,1', 'term2': None}) == '1.1'
assert tmpl.render({'term1': '1.1', 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': 1.1, 'term2': '2,2'}) == '3.3'
assert tmpl.render({'term1': 0, 'term2': '2,2'}) == '2.2'
assert tmpl.render({'term1': '', 'term2': '2,2'}) == '2.2'
assert tmpl.render({'term1': None, 'term2': '2,2'}) == '2.2'
# fallback to Django native add filter
assert tmpl.render({'term1': 'foo', 'term2': 'bar'}) == 'foobar'
assert tmpl.render({'term1': 'foo', 'term2': ''}) == 'foo'
assert tmpl.render({'term1': 'foo', 'term2': None}) == 'foo'
assert tmpl.render({'term1': '', 'term2': 'bar'}) == 'bar'
assert tmpl.render({'term1': '', 'term2': None}) == ''
assert tmpl.render({'term1': None, 'term2': 'bar'}) == 'bar'
assert tmpl.render({'term1': None, 'term2': ''}) == ''
assert tmpl.render({'term1': None, 'term2': None}) == ''
# subtract
tmpl = Template('{{ term1|subtract:term2 }}')
assert tmpl.render({'term1': 5.1, 'term2': 1}) == '4.1'

View File

@ -147,7 +147,7 @@ def time(value, arg=None):
return defaultfilters.date(value, arg=arg)
def parse_decimal(value):
def parse_decimal(value, do_raise=False):
if hasattr(value, 'get_value'):
value = value.get_value() # unlazy
if isinstance(value, six.string_types):
@ -156,6 +156,8 @@ def parse_decimal(value):
try:
return Decimal(value).quantize(Decimal('1.0000')).normalize()
except (ArithmeticError, TypeError):
if do_raise:
raise
return Decimal(0)
@ -277,7 +279,37 @@ def action_button(context, action_id, label, delay=3):
@register.filter
def add(term1, term2):
'''replace the "add" native django filter'''
return parse_decimal(term1) + parse_decimal(term2)
# consider None content as the empty string
if term1 is None:
term1 = ''
if term2 is None:
term2 = ''
# return available number if the other term is the empty string
if term1 == '':
try:
return parse_decimal(term2, do_raise=True)
except (ArithmeticError, TypeError):
pass
if term2 == '':
try:
return parse_decimal(term1, do_raise=True)
except (ArithmeticError, TypeError):
pass
# compute addition if both terms are numbers
try:
return parse_decimal(term1, do_raise=True) + parse_decimal(term2, do_raise=True)
except (ArithmeticError, TypeError):
pass
# fallback to django add filter
if hasattr(term1, 'get_value'):
term1 = term1.get_value() # unlazy
if hasattr(term2, 'get_value'):
term2 = term2.get_value() # unlazy
return defaultfilters.add(term1, term2)
@register.filter