Explorar el Código

feat(utilities): Add ranges_to_string_list

Introduce `ranges_to_string_list` for converting numeric ranges into a
list of readable strings. Update the `vid_ranges_list` property and
templates to use this method for better readability and maintainability.
Add related tests to ensure functionality.

Closes #20516
Martin Hauser hace 4 meses
padre
commit
cfbd9632ac

+ 12 - 2
netbox/ipam/models/vlans.py

@@ -10,9 +10,9 @@ from django.utils.translation import gettext_lazy as _
 from dcim.models import Interface, Site, SiteGroup
 from ipam.choices import *
 from ipam.constants import *
-from ipam.querysets import VLANQuerySet, VLANGroupQuerySet
+from ipam.querysets import VLANGroupQuerySet, VLANQuerySet
 from netbox.models import OrganizationalModel, PrimaryModel, NetBoxModel
-from utilities.data import check_ranges_overlap, ranges_to_string
+from utilities.data import check_ranges_overlap, ranges_to_string, ranges_to_string_list
 from virtualization.models import VMInterface
 
 __all__ = (
@@ -164,8 +164,18 @@ class VLANGroup(OrganizationalModel):
         """
         return VLAN.objects.filter(group=self).order_by('vid')
 
+    @property
+    def vid_ranges_items(self):
+        """
+        Property that converts VID ranges to a list of string representations.
+        """
+        return ranges_to_string_list(self.vid_ranges)
+
     @property
     def vid_ranges_list(self):
+        """
+        Property that converts VID ranges into a string representation.
+        """
         return ranges_to_string(self.vid_ranges)
 
 

+ 2 - 1
netbox/ipam/tables/vlans.py

@@ -41,7 +41,8 @@ class VLANGroupTable(TenancyColumnsMixin, NetBoxTable):
         linkify=True,
         orderable=False
     )
-    vid_ranges_list = tables.Column(
+    vid_ranges_list = columns.ArrayColumn(
+        accessor='vid_ranges_items',
         verbose_name=_('VID Ranges'),
         orderable=False
     )

+ 1 - 1
netbox/templates/ipam/vlangroup.html

@@ -40,7 +40,7 @@
         </tr>
         <tr>
           <th scope="row">{% trans "VLAN IDs" %}</th>
-          <td>{{ object.vid_ranges_list }}</td>
+          <td>{{ object.vid_ranges_items|join:", " }}</td>
         </tr>
         <tr>
           <th scope="row">Utilization</th>

+ 33 - 10
netbox/utilities/data.py

@@ -1,7 +1,8 @@
 import decimal
-from django.db.backends.postgresql.psycopg_any import NumericRange
 from itertools import count, groupby
 
+from django.db.backends.postgresql.psycopg_any import NumericRange
+
 __all__ = (
     'array_to_ranges',
     'array_to_string',
@@ -10,6 +11,7 @@ __all__ = (
     'drange',
     'flatten_dict',
     'ranges_to_string',
+    'ranges_to_string_list',
     'shallow_compare_dict',
     'string_to_ranges',
 )
@@ -73,8 +75,10 @@ def shallow_compare_dict(source_dict, destination_dict, exclude=tuple()):
 def array_to_ranges(array):
     """
     Convert an arbitrary array of integers to a list of consecutive values. Nonconsecutive values are returned as
-    single-item tuples. For example:
-        [0, 1, 2, 10, 14, 15, 16] => [(0, 2), (10,), (14, 16)]"
+    single-item tuples.
+
+    Example:
+        [0, 1, 2, 10, 14, 15, 16] => [(0, 2), (10,), (14, 16)]
     """
     group = (
         list(x) for _, x in groupby(sorted(array), lambda x, c=count(): next(c) - x)
@@ -87,7 +91,8 @@ def array_to_ranges(array):
 def array_to_string(array):
     """
     Generate an efficient, human-friendly string from a set of integers. Intended for use with ArrayField.
-    For example:
+
+    Example:
         [0, 1, 2, 10, 14, 15, 16] => "0-2, 10, 14-16"
     """
     ret = []
@@ -135,6 +140,29 @@ def check_ranges_overlap(ranges):
     return False
 
 
+def ranges_to_string_list(ranges):
+    """
+    Convert numeric ranges to a list of display strings.
+
+    Each range is rendered as "lower-upper" or "lower" (for singletons).
+    Bounds are normalized to inclusive values using ``lower_inc``/``upper_inc``.
+    This underpins ``ranges_to_string()``, which joins the result with commas.
+
+    Example:
+        [NumericRange(1, 6), NumericRange(8, 9), NumericRange(10, 13)] => ["1-5", "8", "10-12"]
+    """
+    if not ranges:
+        return []
+
+    output: list[str] = []
+    for r in ranges:
+        # Compute inclusive bounds regardless of how the DB range is stored.
+        lower = r.lower if r.lower_inc else r.lower + 1
+        upper = r.upper if r.upper_inc else r.upper - 1
+        output.append(f"{lower}-{upper}" if lower != upper else str(lower))
+    return output
+
+
 def ranges_to_string(ranges):
     """
     Converts a list of ranges into a string representation.
@@ -151,12 +179,7 @@ def ranges_to_string(ranges):
     """
     if not ranges:
         return ''
-    output = []
-    for r in ranges:
-        lower = r.lower if r.lower_inc else r.lower + 1
-        upper = r.upper if r.upper_inc else r.upper - 1
-        output.append(f"{lower}-{upper}" if lower != upper else str(lower))
-    return ','.join(output)
+    return ','.join(ranges_to_string_list(ranges))
 
 
 def string_to_ranges(value):

+ 19 - 3
netbox/utilities/tests/test_data.py

@@ -1,7 +1,11 @@
 from django.db.backends.postgresql.psycopg_any import NumericRange
 from django.test import TestCase
-
-from utilities.data import check_ranges_overlap, ranges_to_string, string_to_ranges
+from utilities.data import (
+    check_ranges_overlap,
+    ranges_to_string,
+    ranges_to_string_list,
+    string_to_ranges,
+)
 
 
 class RangeFunctionsTestCase(TestCase):
@@ -47,14 +51,26 @@ class RangeFunctionsTestCase(TestCase):
             ])
         )
 
+    def test_ranges_to_string_list(self):
+        self.assertEqual(
+            ranges_to_string_list([
+                NumericRange(10, 20),    # 10-19
+                NumericRange(30, 40),    # 30-39
+                NumericRange(50, 51),    # 50-50
+                NumericRange(100, 200),  # 100-199
+            ]),
+            ['10-19', '30-39', '50', '100-199']
+        )
+
     def test_ranges_to_string(self):
         self.assertEqual(
             ranges_to_string([
                 NumericRange(10, 20),    # 10-19
                 NumericRange(30, 40),    # 30-39
+                NumericRange(50, 51),    # 50-50
                 NumericRange(100, 200),  # 100-199
             ]),
-            '10-19,30-39,100-199'
+            '10-19,30-39,50,100-199'
         )
 
     def test_string_to_ranges(self):