diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index c29caa189..1209153bc 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -77,9 +77,7 @@ class Region(MPTTModel): # class SiteManager(NaturalOrderByManager): - - def get_queryset(self): - return self.natural_order_by('name') + natural_order_field = 'name' @python_2_unicode_compatible @@ -308,9 +306,7 @@ class RackRole(models.Model): class RackManager(NaturalOrderByManager): - - def get_queryset(self): - return self.natural_order_by('site__name', 'name') + natural_order_field = 'name' @python_2_unicode_compatible @@ -1098,9 +1094,7 @@ class Platform(models.Model): class DeviceManager(NaturalOrderByManager): - - def get_queryset(self): - return self.natural_order_by('name') + natural_order_field = 'name' @python_2_unicode_compatible diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index e71395ebc..159c70db5 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -175,7 +175,7 @@ class RegionTable(BaseTable): class SiteTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn() + name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3')) status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') region = tables.TemplateColumn(template_code=SITE_REGION_LINK) tenant = tables.TemplateColumn(template_code=COL_TENANT) @@ -236,7 +236,7 @@ class RackRoleTable(BaseTable): class RackTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn() + name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3')) site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') tenant = tables.TemplateColumn(template_code=COL_TENANT) @@ -469,7 +469,10 @@ class PlatformTable(BaseTable): class DeviceTable(BaseTable): pk = ToggleColumn() - name = tables.TemplateColumn(template_code=DEVICE_LINK) + name = tables.TemplateColumn( + order_by=('_nat1', '_nat2', '_nat3'), + template_code=DEVICE_LINK + ) status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status') tenant = tables.TemplateColumn(template_code=COL_TENANT) site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) diff --git a/netbox/utilities/managers.py b/netbox/utilities/managers.py index dc882d462..b1fb09f9d 100644 --- a/netbox/utilities/managers.py +++ b/netbox/utilities/managers.py @@ -4,29 +4,35 @@ from django.db.models import Manager class NaturalOrderByManager(Manager): + """ + Order objects naturally by a designated field. Leading and/or trailing digits of values within this field will be + cast as independent integers and sorted accordingly. For example, "Foo2" will be ordered before "Foo10", even though + the digit 1 is normally ordered before the digit 2. + """ + natural_order_field = None - def natural_order_by(self, *fields): - """ - Attempt to order records naturally by segmenting a field into three parts: + def get_queryset(self): - 1. Leading integer (if any) - 2. Middle portion - 3. Trailing integer (if any) + queryset = super(NaturalOrderByManager, self).get_queryset() - :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] + db_field = self.natural_order_field - 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{{1,9}})') AS integer)".format(db_table, primary_field), - id2: "SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, primary_field), - id3: "CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)".format(db_table, primary_field), + # Append the three subfields derived from the designated natural ordering field + queryset = queryset.extra(select={ + '_nat1': "CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)".format(db_table, db_field), + '_nat2': "SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, db_field), + '_nat3': "CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)".format(db_table, db_field), }) - ordering = fields[0:-1] + (id1, id2, id3) + + # Replace any instance of the designated natural ordering field with its three subfields + ordering = [] + for field in self.model._meta.ordering: + if field == self.natural_order_field: + ordering.append('_nat1') + ordering.append('_nat2') + ordering.append('_nat3') + else: + ordering.append(field) return queryset.order_by(*ordering)