Browse Source

#303: First stab at implementing a natural ordering for sites, racks, and devices

Jeremy Stretch 8 years ago
parent
commit
b8d7dd170e
3 changed files with 57 additions and 1 deletions
  1. 26 0
      netbox/dcim/models.py
  2. 1 1
      netbox/dcim/views.py
  3. 30 0
      netbox/utilities/managers.py

+ 26 - 0
netbox/dcim/models.py

@@ -9,10 +9,12 @@ from django.db.models import Count, Q, ObjectDoesNotExist
 
 from extras.rpc import RPC_CLIENTS
 from utilities.fields import NullableCharField
+from utilities.managers import NaturalOrderByManager
 from utilities.models import CreatedUpdatedModel
 
 from .fields import ASNField, MACAddressField
 
+
 RACK_FACE_FRONT = 0
 RACK_FACE_REAR = 1
 RACK_FACE_CHOICES = [
@@ -137,6 +139,12 @@ def order_interfaces(queryset, sql_col, primary_ordering=tuple()):
     }).order_by(*ordering)
 
 
+class SiteManager(NaturalOrderByManager):
+
+    def get_queryset(self):
+        return self.natural_order_by('name')
+
+
 class Site(CreatedUpdatedModel):
     """
     A Site represents a geographic location within a network; typically a building or campus. The optional facility
@@ -150,6 +158,8 @@ class Site(CreatedUpdatedModel):
     shipping_address = models.CharField(max_length=200, blank=True)
     comments = models.TextField(blank=True)
 
+    objects = SiteManager()
+
     class Meta:
         ordering = ['name']
 
@@ -212,6 +222,12 @@ class RackGroup(models.Model):
         return "{}?group_id={}".format(reverse('dcim:rack_list'), self.pk)
 
 
+class RackManager(NaturalOrderByManager):
+
+    def get_queryset(self):
+        return self.natural_order_by('site__name', 'name')
+
+
 class Rack(CreatedUpdatedModel):
     """
     Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face.
@@ -224,6 +240,8 @@ class Rack(CreatedUpdatedModel):
     u_height = models.PositiveSmallIntegerField(default=42, verbose_name='Height (U)')
     comments = models.TextField(blank=True)
 
+    objects = RackManager()
+
     class Meta:
         ordering = ['site', 'name']
         unique_together = [
@@ -583,6 +601,12 @@ class Platform(models.Model):
         return "{}?platform={}".format(reverse('dcim:device_list'), self.slug)
 
 
+class DeviceManager(NaturalOrderByManager):
+
+    def get_queryset(self):
+        return self.natural_order_by('name')
+
+
 class Device(CreatedUpdatedModel):
     """
     A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
@@ -612,6 +636,8 @@ class Device(CreatedUpdatedModel):
                                        blank=True, null=True, verbose_name='Primary IPv6')
     comments = models.TextField(blank=True)
 
+    objects = DeviceManager()
+
     class Meta:
         ordering = ['name']
         unique_together = ['rack', 'position', 'face']

+ 1 - 1
netbox/dcim/views.py

@@ -144,7 +144,7 @@ class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 #
 
 class RackListView(ObjectListView):
-    queryset = Rack.objects.select_related('site').annotate(device_count=Count('devices', distinct=True))
+    queryset = Rack.objects.select_related('site', 'group').annotate(device_count=Count('devices', distinct=True))
     filter = filters.RackFilter
     filter_form = forms.RackFilterForm
     table = tables.RackTable

+ 30 - 0
netbox/utilities/managers.py

@@ -0,0 +1,30 @@
+from django.db.models import Manager
+
+
+class NaturalOrderByManager(Manager):
+
+    def natural_order_by(self, *fields):
+        """
+        Attempt to order records naturally by segmenting a field into three parts:
+
+        1. Leading integer (if any)
+        2. Middle portion
+        3. Trailing integer (if any)
+
+        :param fields: The fields on which to order the queryset. The last field in the list will be ordered naturally.
+        """
+        db_table = self.model._meta.db_table
+        primary_field = fields[-1]
+
+        id1 = '_{}_{}1'.format(db_table, primary_field)
+        id2 = '_{}_{}2'.format(db_table, primary_field)
+        id3 = '_{}_{}3'.format(db_table, primary_field)
+
+        queryset = super(NaturalOrderByManager, self).get_queryset().extra(select={
+            id1: "CAST(SUBSTRING({}.{} FROM '^(\d+)') AS integer)".format(db_table, primary_field),
+            id2: "SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, primary_field),
+            id3: "CAST(SUBSTRING({}.{} FROM '(\d+)$') AS integer)".format(db_table, primary_field),
+        })
+        ordering = fields[0:-1] + (id1, id2, id3)
+
+        return queryset.order_by(*ordering)