diff --git a/netbox/dcim/migrations/0209_alter_macaddress_options.py b/netbox/dcim/migrations/0209_alter_macaddress_options.py new file mode 100644 index 000000000..2f3b6de06 --- /dev/null +++ b/netbox/dcim/migrations/0209_alter_macaddress_options.py @@ -0,0 +1,19 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0208_devicerole_uniqueness'), + ] + + operations = [ + migrations.AlterModelOptions( + name='macaddress', + options={ + 'ordering': ('mac_address', 'pk'), + 'verbose_name': 'MAC address', + 'verbose_name_plural': 'MAC addresses' + }, + ), + ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index abd29d4e3..e1ad9c15c 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -1276,7 +1276,7 @@ class MACAddress(PrimaryModel): ) class Meta: - ordering = ('mac_address',) + ordering = ('mac_address', 'pk',) verbose_name = _('MAC address') verbose_name_plural = _('MAC addresses') diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index be9f067d4..103795766 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -37,6 +37,19 @@ class MACAddressTestCase(TestCase): cls.interface.primary_mac_address = cls.mac_a cls.interface.save() + shared_mac = '1234567890ab' + interfaces = [] + for i in range(10): + interfaces.append(Interface( + device=device, name=f'Interface {i:03d}', type='1000base-t' + )) + Interface.objects.bulk_create(interfaces) + + mac_addresses = [] + for interface in interfaces: + mac_addresses.append(MACAddress(mac_address=shared_mac, assigned_object=interface)) + MACAddress.objects.bulk_create(mac_addresses) + @tag('regression') def test_clean_will_not_allow_removal_of_assigned_object_if_primary(self): self.mac_a.assigned_object = None @@ -48,6 +61,29 @@ class MACAddressTestCase(TestCase): self.mac_b.assigned_object = None self.mac_b.clean() + @tag('regression') + def test_mac_address_ordering_with_duplicates(self): + """ + Test that MAC addresses with the same value are ordered by their primary key (pk). + This ensures deterministic ordering for pagination when multiple records share the same MAC address. + """ + mac_addresses = list(MACAddress.objects.all()) + + mac_address_groups = {} + for mac in mac_addresses: + if mac.mac_address not in mac_address_groups: + mac_address_groups[mac.mac_address] = [] + mac_address_groups[mac.mac_address].append(mac) + + for mac_value, macs in mac_address_groups.items(): + if len(macs) > 1: + for i in range(1, len(macs)): + self.assertLess( + macs[i - 1].pk, + macs[i].pk, + f"MAC addresses with value {mac_value} are not ordered by primary key" + ) + class LocationTestCase(TestCase):