Fixed #20601 -- Allowed forcing format with thousand separators in floatformat filter.

Thanks Claude Paroz and Nick Pope for reviews.
This commit is contained in:
Jacob Walls 2020-10-11 23:34:32 -04:00 committed by Mariusz Felisiak
parent e18156b6c3
commit ac6c426007
5 changed files with 74 additions and 3 deletions

View File

@ -119,9 +119,20 @@ def floatformat(text, arg=-1):
* {{ num2|floatformat:"-3" }} displays "34" * {{ num2|floatformat:"-3" }} displays "34"
* {{ num3|floatformat:"-3" }} displays "34.260" * {{ num3|floatformat:"-3" }} displays "34.260"
If arg has the 'g' suffix, force the result to be grouped by the
THOUSAND_SEPARATOR for the active locale. When the active locale is
en (English):
* {{ 6666.6666|floatformat:"2g" }} displays "6,666.67"
* {{ 10000|floatformat:"g" }} displays "10,000"
If the input float is infinity or NaN, display the string representation If the input float is infinity or NaN, display the string representation
of that value. of that value.
""" """
force_grouping = False
if isinstance(arg, str) and arg.endswith('g'):
force_grouping = True
arg = arg[:-1] or -1
try: try:
input_val = repr(text) input_val = repr(text)
d = Decimal(input_val) d = Decimal(input_val)
@ -141,7 +152,9 @@ def floatformat(text, arg=-1):
return input_val return input_val
if not m and p < 0: if not m and p < 0:
return mark_safe(formats.number_format('%d' % (int(d)), 0)) return mark_safe(
formats.number_format('%d' % (int(d)), 0, force_grouping=force_grouping),
)
exp = Decimal(1).scaleb(-abs(p)) exp = Decimal(1).scaleb(-abs(p))
# Set the precision high enough to avoid an exception (#15789). # Set the precision high enough to avoid an exception (#15789).
@ -161,7 +174,9 @@ def floatformat(text, arg=-1):
if sign and rounded_d: if sign and rounded_d:
digits.append('-') digits.append('-')
number = ''.join(reversed(digits)) number = ''.join(reversed(digits))
return mark_safe(formats.number_format(number, abs(p))) return mark_safe(
formats.number_format(number, abs(p), force_grouping=force_grouping),
)
@register.filter(is_safe=True) @register.filter(is_safe=True)

View File

@ -1732,6 +1732,18 @@ displayed. For example:
``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260`` ``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260``
============ ================================ ========== ============ ================================ ==========
If the argument passed to ``floatformat`` has the ``g`` suffix, it will force
grouping by the :setting:`THOUSAND_SEPARATOR` for the active locale. For
example, when the active locale is ``en`` (English):
============ ================================= =============
``value`` Template Output
============ ================================= =============
``34232.34`` ``{{ value|floatformat:"2g" }}`` ``34,232.34``
``34232.06`` ``{{ value|floatformat:"g" }}`` ``34,232.1``
``34232.00`` ``{{ value|floatformat:"-3g" }}`` ``34,232``
============ ================================= =============
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` Using ``floatformat`` with no argument is equivalent to using ``floatformat``
with an argument of ``-1``. with an argument of ``-1``.
@ -1740,6 +1752,10 @@ with an argument of ``-1``.
In older versions, a negative zero ``-0`` was returned for negative numbers In older versions, a negative zero ``-0`` was returned for negative numbers
which round to zero. which round to zero.
.. versionchanged:: 3.2
The ``g`` suffix to force grouping by thousand separators was added.
.. templatefilter:: force_escape .. templatefilter:: force_escape
``force_escape`` ``force_escape``

View File

@ -367,7 +367,8 @@ Signals
Templates Templates
~~~~~~~~~ ~~~~~~~~~
* ... * :tfilter:`floatformat` template filter now allows using the ``g`` suffix to
force grouping by the :setting:`THOUSAND_SEPARATOR` for the active locale.
Tests Tests
~~~~~ ~~~~~

View File

@ -560,6 +560,14 @@ class FormattingTests(SimpleTestCase):
self.assertEqual('des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual('des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt))
self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
self.assertEqual(
'66666.67',
Template('{{ n|floatformat:"2g" }}').render(self.ctxt),
)
self.assertEqual(
'100000.0',
Template('{{ f|floatformat:"g" }}').render(self.ctxt),
)
self.assertEqual('10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) self.assertEqual('10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual( self.assertEqual(
@ -734,6 +742,14 @@ class FormattingTests(SimpleTestCase):
self.assertEqual('31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt)) self.assertEqual('31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt))
self.assertEqual('66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual('66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual('100000,0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual('100000,0', Template('{{ f|floatformat }}').render(self.ctxt))
self.assertEqual(
'66.666,67',
Template('{{ n|floatformat:"2g" }}').render(self.ctxt),
)
self.assertEqual(
'100.000,0',
Template('{{ f|floatformat:"g" }}').render(self.ctxt),
)
self.assertEqual('10:15', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) self.assertEqual('10:15', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual('31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual('31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual( self.assertEqual(
@ -935,6 +951,14 @@ class FormattingTests(SimpleTestCase):
self.assertEqual('Dec. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual('Dec. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt))
self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual('66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual('100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
self.assertEqual(
'66,666.67',
Template('{{ n|floatformat:"2g" }}').render(self.ctxt),
)
self.assertEqual(
'100,000.0',
Template('{{ f|floatformat:"g" }}').render(self.ctxt),
)
self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual( self.assertEqual(
'12/31/2009 8:50 p.m.', '12/31/2009 8:50 p.m.',

View File

@ -2,6 +2,8 @@ from decimal import Decimal, localcontext
from django.template.defaultfilters import floatformat from django.template.defaultfilters import floatformat
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.test.utils import override_settings
from django.utils import translation
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from ..utils import setup from ..utils import setup
@ -58,6 +60,19 @@ class FunctionTests(SimpleTestCase):
self.assertEqual(floatformat(1.5e-15, -20), '0.00000000000000150000') self.assertEqual(floatformat(1.5e-15, -20), '0.00000000000000150000')
self.assertEqual(floatformat(1.00000000000000015, 16), '1.0000000000000002') self.assertEqual(floatformat(1.00000000000000015, 16), '1.0000000000000002')
@override_settings(USE_L10N=True)
def test_force_grouping(self):
with translation.override('en'):
self.assertEqual(floatformat(10000, 'g'), '10,000')
self.assertEqual(floatformat(66666.666, '1g'), '66,666.7')
# Invalid suffix.
self.assertEqual(floatformat(10000, 'g2'), '10000')
with translation.override('de', deactivate=True):
self.assertEqual(floatformat(10000, 'g'), '10.000')
self.assertEqual(floatformat(66666.666, '1g'), '66.666,7')
# Invalid suffix.
self.assertEqual(floatformat(10000, 'g2'), '10000')
def test_zero_values(self): def test_zero_values(self):
self.assertEqual(floatformat(0, 6), '0.000000') self.assertEqual(floatformat(0, 6), '0.000000')
self.assertEqual(floatformat(0, 7), '0.0000000') self.assertEqual(floatformat(0, 7), '0.0000000')