diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 52527cc19..311f02e99 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -661,28 +661,32 @@ class InterfaceQuerySet(models.QuerySet): """ sql_col = '{}.name'.format(self.model._meta.db_table) ordering = { - IFACE_ORDERING_POSITION: ('_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_type', 'name'), - IFACE_ORDERING_NAME: ('_type', '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', 'name'), + IFACE_ORDERING_POSITION: ( + '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_type', '_id', 'name', + ), + IFACE_ORDERING_NAME: ( + '_type', '_slot', '_subslot', '_position', '_subposition', '_channel', '_vc', '_id', 'name', + ), }[method] + TYPE_RE = r"SUBSTRING({} FROM '^([^0-9]+)')" + ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)$') AS integer)" + SLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)\/') AS integer)" + SUBSLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/)([0-9]+)') AS integer)" + POSITION_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{2}}([0-9]+)') AS integer)" + SUBPOSITION_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{3}}([0-9]+)') AS integer)" + CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM ':([0-9]+)(\.[0-9]+)?$') AS integer), 0)" + VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '\.([0-9]+)$') AS integer), 0)" + fields = { - '_type': RawSQL(r"SUBSTRING({} FROM '^([^0-9]+)')".format(sql_col), []), - '_slot': RawSQL(r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)') AS integer)".format(sql_col), []), - '_subslot': RawSQL( - r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+)\/([0-9]+)') AS integer), 0)".format( - sql_col), [] - ), - '_position': RawSQL( - r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{2}}([0-9]+)') AS integer), 0)".format( - sql_col), [] - ), - '_subposition': RawSQL( - r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(?:[0-9]+\/){{3}}([0-9]+)') AS integer), 0)".format( - sql_col), [] - ), - '_channel': RawSQL( - r"COALESCE(CAST(SUBSTRING({} FROM ':([0-9]+)(\.[0-9]+)?$') AS integer), 0)".format(sql_col), []), - '_vc': RawSQL(r"COALESCE(CAST(SUBSTRING({} FROM '\.([0-9]+)$') AS integer), 0)".format(sql_col), []), + '_type': RawSQL(TYPE_RE.format(sql_col), []), + '_id': RawSQL(ID_RE.format(sql_col), []), + '_slot': RawSQL(SLOT_RE.format(sql_col), []), + '_subslot': RawSQL(SUBSLOT_RE.format(sql_col), []), + '_position': RawSQL(POSITION_RE.format(sql_col), []), + '_subposition': RawSQL(SUBPOSITION_RE.format(sql_col), []), + '_channel': RawSQL(CHANNEL_RE.format(sql_col), []), + '_vc': RawSQL(VC_RE.format(sql_col), []), } return self.annotate(**fields).order_by(*ordering) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index 00a4de28c..b4101cfd5 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -159,10 +159,14 @@ class InterfaceTestCase(TestCase): device=device1, name='Ethernet1/3/2/1' ) + interface6 = Interface.objects.create( + device=device1, + name='Loopback1' + ) self.assertEqual( list(Interface.objects.all().order_naturally()), - [interface1, interface5, interface4, interface3, interface2] + [interface1, interface5, interface4, interface3, interface2, interface6] ) def test_interface_order_natural_subinterfaces(self): @@ -201,5 +205,5 @@ class InterfaceTestCase(TestCase): ) self.assertEqual( list(Interface.objects.all().order_naturally()), - [interface6, interface4, interface3, interface5, interface2, interface1] + [interface4, interface3, interface5, interface2, interface1, interface6] )