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"
     * {{ 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)

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

@@ -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``

+ 2 - 1
docs/releases/3.2.txt

@@ -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
 ~~~~~
 ~~~~~

+ 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('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.',

+ 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.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')