Просмотр исходного кода

Fix measurement unit preference display issues

- Read preferences from original template context in ObjectAttributesPanel
  (was reading from the panel's own prepared ctx dict, which never included it)
- Add ui.measurement_system to UserConfigForm fieldset so it appears in the
  Preferences UI
- Use unit code abbreviations (kg, g, lb/lbs, oz) in WeightAttr and DistanceAttr
  else branches instead of the verbose choice display label
- Apply floatformat before pluralize in total_weight templates to avoid
  floating-point values like 0.999998 incorrectly triggering the plural form
- Fix pluralize usage: pass formatted string to pluralize rather than raw float
- Update unit tests to reflect abbreviation-based output

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brian Tiemann 2 дней назад
Родитель
Сommit
7c8f41aee4

+ 22 - 19
netbox/netbox/tests/test_ui.py

@@ -1,6 +1,6 @@
 from types import SimpleNamespace
 
-from django.test import RequestFactory, TestCase
+from django.test import RequestFactory, SimpleTestCase, TestCase
 
 from circuits.choices import CircuitStatusChoices, VirtualCircuitTerminationRoleChoices
 from circuits.models import (
@@ -461,7 +461,7 @@ class DateTimeAttrTestCase(TestCase):
         self.assertEqual(context['spec'], 'minutes')
 
 
-class WeightAttrTestCase(TestCase):
+class WeightAttrTestCase(SimpleTestCase):
 
     def _ctx(self, system=''):
         return {'name': 'weight', 'preferences': {'ui.measurement_system': system}}
@@ -485,7 +485,7 @@ class WeightAttrTestCase(TestCase):
         obj = self._obj(5, 'kg', 5000, 'Kilograms')
         result = attr.render(obj, self._ctx(system=''))
         self.assertIn('5', result)
-        self.assertIn('kilograms', result)
+        self.assertIn('kg', result)
 
     def test_metric_converts_lbs_to_kg(self):
         # 10 lb = 4535.92 g → 4535.92 / 1000 = 4.54 kg
@@ -500,7 +500,7 @@ class WeightAttrTestCase(TestCase):
         obj = self._obj(5, 'kg', 5000, 'Kilograms')
         result = attr.render(obj, self._ctx(system='metric'))
         self.assertIn('5', result)
-        self.assertIn('kilograms', result)
+        self.assertIn('kg', result)
 
     def test_imperial_converts_kg_to_lbs(self):
         # 1 kg = 1000 g → 1000 / 453.592 = 2.2 lbs
@@ -510,26 +510,32 @@ class WeightAttrTestCase(TestCase):
         self.assertIn('2.2', result)
         self.assertIn('lbs', result)
 
+    def test_imperial_converts_kg_to_singular_lb(self):
+        # 453.592 g = exactly 1.0 lb → singular 'lb'
+        attr = attrs.WeightAttr('weight')
+        obj = self._obj(1, 'kg', 453.592, 'Kilograms')
+        result = attr.render(obj, self._ctx(system='imperial'))
+        self.assertIn('1.0', result)
+        self.assertIn('lb', result)
+        self.assertNotIn('lbs', result)
+
     def test_imperial_no_conversion_for_imperial_unit(self):
         attr = attrs.WeightAttr('weight')
         obj = self._obj(10, 'lb', 4535.92, 'Pounds')
         result = attr.render(obj, self._ctx(system='imperial'))
         self.assertIn('10', result)
-        self.assertIn('pounds', result)
+        self.assertIn('lbs', result)
 
     def test_metric_no_conversion_when_abs_weight_is_none(self):
         # abs_weight=None → falsy → falls through to stored value
         attr = attrs.WeightAttr('weight')
-        obj = SimpleNamespace(
-            weight=10, weight_unit='lb', _abs_weight=None,
-            get_weight_unit_display=lambda: 'Pounds',
-        )
+        obj = SimpleNamespace(weight=10, weight_unit='lb', _abs_weight=None)
         result = attr.render(obj, self._ctx(system='metric'))
         self.assertIn('10', result)
-        self.assertIn('pounds', result)
+        self.assertIn('lbs', result)
 
 
-class DistanceAttrTestCase(TestCase):
+class DistanceAttrTestCase(SimpleTestCase):
 
     def _ctx(self, system=''):
         return {'name': 'distance', 'preferences': {'ui.measurement_system': system}}
@@ -553,7 +559,7 @@ class DistanceAttrTestCase(TestCase):
         obj = self._obj(10, 'km', 10000, 'Kilometers')
         result = attr.render(obj, self._ctx(system=''))
         self.assertIn('10', result)
-        self.assertIn('kilometers', result)
+        self.assertIn('km', result)
 
     def test_metric_converts_ft_to_m_under_threshold(self):
         # 500 ft = 152.4 m (< 1000) → display in m
@@ -593,25 +599,22 @@ class DistanceAttrTestCase(TestCase):
         obj = self._obj(10, 'km', 10000, 'Kilometers')
         result = attr.render(obj, self._ctx(system='metric'))
         self.assertIn('10', result)
-        self.assertIn('kilometers', result)
+        self.assertIn('km', result)
 
     def test_imperial_no_conversion_for_imperial_unit(self):
         attr = attrs.DistanceAttr('distance')
         obj = self._obj(10, 'mi', 16093.44, 'Miles')
         result = attr.render(obj, self._ctx(system='imperial'))
         self.assertIn('10', result)
-        self.assertIn('miles', result)
+        self.assertIn('mi', result)
 
     def test_metric_no_conversion_when_abs_distance_is_none(self):
         # abs_distance=None → falls through to stored value
         attr = attrs.DistanceAttr('distance')
-        obj = SimpleNamespace(
-            distance=10, distance_unit='ft', _abs_distance=None,
-            get_distance_unit_display=lambda: 'Feet',
-        )
+        obj = SimpleNamespace(distance=10, distance_unit='ft', _abs_distance=None)
         result = attr.render(obj, self._ctx(system='metric'))
         self.assertIn('10', result)
-        self.assertIn('feet', result)
+        self.assertIn('ft', result)
 
 
 class ObjectsTablePanelTestCase(TestCase):

+ 3 - 3
netbox/netbox/ui/attrs.py

@@ -600,10 +600,10 @@ class WeightAttr(ObjectAttribute):
             display_unit = 'kg'
         elif system == 'imperial' and unit in _METRIC_WEIGHT and abs_weight:
             display_value = round(abs_weight / 453.592, 2)
-            display_unit = 'lbs'
+            display_unit = 'lb' if display_value == 1 else 'lbs'
         else:
             display_value = weight
-            display_unit = resolve_attr_path(obj, 'get_weight_unit_display')().lower()
+            display_unit = 'lb' if (unit == 'lb' and display_value == 1) else ('lbs' if unit == 'lb' else unit)
 
         return render_to_string(self.template_name, {
             'name': context['name'],
@@ -654,7 +654,7 @@ class DistanceAttr(ObjectAttribute):
                 display_unit = 'ft'
         else:
             display_value = distance
-            display_unit = resolve_attr_path(obj, 'get_distance_unit_display')().lower()
+            display_unit = unit  # 'km', 'm', 'mi', 'ft' are standard abbreviations
 
         return render_to_string(self.template_name, {
             'name': context['name'],

+ 1 - 1
netbox/netbox/ui/panels.py

@@ -208,7 +208,7 @@ class ObjectAttributesPanel(ObjectPanel, metaclass=ObjectAttributesPanelMeta):
                     'value': attr.render(ctx['object'], {
                         'name': name,
                         'perms': ctx['perms'],
-                        'preferences': ctx.get('preferences', {}),
+                        'preferences': context.get('preferences', {}),
                     }),
                 } for name, attr in self._attrs.items() if name in attr_names
             ],

+ 3 - 4
netbox/templates/dcim/device/attrs/total_weight.html

@@ -1,11 +1,10 @@
 {% load helpers i18n %}
 {% with system=preferences|get_key:"ui.measurement_system" %}
 {% if system == "imperial" %}
-{{ value|kg_to_pounds|floatformat }} {% trans "Pounds" %}
+{% with lbs=value|kg_to_pounds|floatformat %}{{ lbs }} {% trans "Pound" %}{{ lbs|pluralize }}{% endwith %}
 {% elif system == "metric" %}
-{{ value|floatformat }} {% trans "Kilograms" %}
+{% with formatted=value|floatformat %}{{ formatted }} {% trans "Kilogram" %}{{ formatted|pluralize }}{% endwith %}
 {% else %}
-{{ value|floatformat }} {% trans "Kilograms" %}
-({{ value|kg_to_pounds|floatformat }} {% trans "Pounds" %})
+{% with formatted=value|floatformat %}{% with lbs=value|kg_to_pounds|floatformat %}{{ formatted }} {% trans "Kilogram" %}{{ formatted|pluralize }} ({{ lbs }} {% trans "Pound" %}{{ lbs|pluralize }}){% endwith %}{% endwith %}
 {% endif %}
 {% endwith %}

+ 3 - 4
netbox/templates/dcim/rack/attrs/total_weight.html

@@ -1,11 +1,10 @@
 {% load helpers i18n %}
 {% with system=preferences|get_key:"ui.measurement_system" %}
 {% if system == "imperial" %}
-{{ value|kg_to_pounds|floatformat }} {% trans "Pounds" %}
+{% with lbs=value|kg_to_pounds|floatformat %}{{ lbs }} {% trans "Pound" %}{{ lbs|pluralize }}{% endwith %}
 {% elif system == "metric" %}
-{{ value|floatformat }} {% trans "Kilograms" %}
+{% with formatted=value|floatformat %}{{ formatted }} {% trans "Kilogram" %}{{ formatted|pluralize }}{% endwith %}
 {% else %}
-{{ value|floatformat }} {% trans "Kilograms" %}
-({{ value|kg_to_pounds|floatformat }} {% trans "Pounds" %})
+{% with formatted=value|floatformat %}{% with lbs=value|kg_to_pounds|floatformat %}{{ formatted }} {% trans "Kilogram" %}{{ formatted|pluralize }} ({{ lbs }} {% trans "Pound" %}{{ lbs|pluralize }}){% endwith %}{% endwith %}
 {% endif %}
 {% endwith %}

+ 1 - 1
netbox/users/forms/model_forms.py

@@ -72,7 +72,7 @@ class UserConfigForm(forms.ModelForm, metaclass=UserConfigFormMetaclass):
     fieldsets = (
         FieldSet(
             'locale.language', 'ui.copilot_enabled', 'pagination.per_page', 'pagination.placement',
-            'ui.tables.striping', name=_('User Interface')
+            'ui.tables.striping', 'ui.measurement_system', name=_('User Interface')
         ),
         FieldSet('data_format', 'csv_delimiter', name=_('Miscellaneous')),
     )