Browse Source

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

Thanks Claude Paroz and Nick Pope for reviews.
Jacob Walls 4 years ago
parent
commit
ac6c426007

+ 17 - 2
django/template/defaultfilters.py

@@ -119,9 +119,20 @@ def floatformat(text, arg=-1):
     * {{ num2|floatformat:"-3" }} displays "34"
     * {{ 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
     of that value.
     """
+    force_grouping = False
+    if isinstance(arg, str) and arg.endswith('g'):
+        force_grouping = True
+        arg = arg[:-1] or -1
     try:
         input_val = repr(text)
         d = Decimal(input_val)
@@ -141,7 +152,9 @@ def floatformat(text, arg=-1):
         return input_val
 
     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))
     # Set the precision high enough to avoid an exception (#15789).
@@ -161,7 +174,9 @@ def floatformat(text, arg=-1):
     if sign and rounded_d:
         digits.append('-')
     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)

+ 16 - 0
docs/ref/templates/builtins.txt

@@ -1732,6 +1732,18 @@ displayed. For example:
 ``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``
 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
     which round to zero.
 
+.. versionchanged:: 3.2
+
+    The ``g`` suffix to force grouping by thousand separators was added.
+
 .. templatefilter:: force_escape
 
 ``force_escape``

+ 2 - 1
docs/releases/3.2.txt

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

+ 24 - 0
tests/i18n/tests.py

@@ -560,6 +560,14 @@ class FormattingTests(SimpleTestCase):
             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('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('12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
             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('66666,67', Template('{{ n|floatformat:2 }}').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('31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
                 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('66666.67', Template('{{ n|floatformat:2 }}').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 8:50 p.m.',

+ 15 - 0
tests/template_tests/filter_tests/test_floatformat.py

@@ -2,6 +2,8 @@ from decimal import Decimal, localcontext
 
 from django.template.defaultfilters import floatformat
 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 ..utils import setup
@@ -58,6 +60,19 @@ class FunctionTests(SimpleTestCase):
         self.assertEqual(floatformat(1.5e-15, -20), '0.00000000000000150000')
         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):
         self.assertEqual(floatformat(0, 6), '0.000000')
         self.assertEqual(floatformat(0, 7), '0.0000000')