diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index feeaf4f28..64028dab3 100644 --- a/netbox/dcim/models.py +++ b/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'] diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 2fdbbd0a7..d59d5496b 100644 --- a/netbox/dcim/views.py +++ b/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 diff --git a/netbox/utilities/managers.py b/netbox/utilities/managers.py new file mode 100644 index 000000000..1235a21d1 --- /dev/null +++ b/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)