mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 12:12:53 -06:00
Merge pull request #2158 from digitalocean/2157-natural-ordering
Fixes #2157: Natural ordering breaks when sorting objects by name
This commit is contained in:
commit
81258ea35b
@ -77,9 +77,7 @@ class Region(MPTTModel):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class SiteManager(NaturalOrderByManager):
|
class SiteManager(NaturalOrderByManager):
|
||||||
|
natural_order_field = 'name'
|
||||||
def get_queryset(self):
|
|
||||||
return self.natural_order_by('name')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
@ -308,9 +306,7 @@ class RackRole(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class RackManager(NaturalOrderByManager):
|
class RackManager(NaturalOrderByManager):
|
||||||
|
natural_order_field = 'name'
|
||||||
def get_queryset(self):
|
|
||||||
return self.natural_order_by('site__name', 'name')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
@ -1098,9 +1094,7 @@ class Platform(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class DeviceManager(NaturalOrderByManager):
|
class DeviceManager(NaturalOrderByManager):
|
||||||
|
natural_order_field = 'name'
|
||||||
def get_queryset(self):
|
|
||||||
return self.natural_order_by('name')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
@ -175,7 +175,7 @@ class RegionTable(BaseTable):
|
|||||||
|
|
||||||
class SiteTable(BaseTable):
|
class SiteTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
||||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -236,7 +236,7 @@ class RackRoleTable(BaseTable):
|
|||||||
|
|
||||||
class RackTable(BaseTable):
|
class RackTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn(order_by=('_nat1', '_nat2', '_nat3'))
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
@ -469,7 +469,10 @@ class PlatformTable(BaseTable):
|
|||||||
|
|
||||||
class DeviceTable(BaseTable):
|
class DeviceTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
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')
|
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||||
|
@ -4,29 +4,35 @@ from django.db.models import Manager
|
|||||||
|
|
||||||
|
|
||||||
class NaturalOrderByManager(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):
|
def get_queryset(self):
|
||||||
"""
|
|
||||||
Attempt to order records naturally by segmenting a field into three parts:
|
|
||||||
|
|
||||||
1. Leading integer (if any)
|
queryset = super(NaturalOrderByManager, self).get_queryset()
|
||||||
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
|
db_table = self.model._meta.db_table
|
||||||
primary_field = fields[-1]
|
db_field = self.natural_order_field
|
||||||
|
|
||||||
id1 = '_{}_{}1'.format(db_table, primary_field)
|
# Append the three subfields derived from the designated natural ordering field
|
||||||
id2 = '_{}_{}2'.format(db_table, primary_field)
|
queryset = queryset.extra(select={
|
||||||
id3 = '_{}_{}3'.format(db_table, primary_field)
|
'_nat1': "CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)".format(db_table, db_field),
|
||||||
|
'_nat2': "SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')".format(db_table, db_field),
|
||||||
queryset = super(NaturalOrderByManager, self).get_queryset().extra(select={
|
'_nat3': "CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)".format(db_table, db_field),
|
||||||
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),
|
|
||||||
})
|
})
|
||||||
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)
|
return queryset.order_by(*ordering)
|
||||||
|
Loading…
Reference in New Issue
Block a user