diff --git a/netbox/dcim/api/serializers_/devices.py b/netbox/dcim/api/serializers_/devices.py index 901f7d08c..4a0f5541b 100644 --- a/netbox/dcim/api/serializers_/devices.py +++ b/netbox/dcim/api/serializers_/devices.py @@ -290,11 +290,13 @@ class MACAddressSerializer(PrimaryModelSerializer): allow_null=True ) assigned_object = GFKSerializerField(read_only=True) + is_primary = serializers.BooleanField(read_only=True) class Meta: model = MACAddress fields = [ 'id', 'url', 'display_url', 'display', 'mac_address', 'assigned_object_type', 'assigned_object_id', - 'assigned_object', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'assigned_object', 'is_primary', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', + 'last_updated', ] brief_fields = ('id', 'url', 'display', 'mac_address', 'description') diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index faac46c39..8da87f8f8 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -1407,9 +1407,10 @@ class MACAddress(PrimaryModel): @cached_property def is_primary(self): - if self.assigned_object and hasattr(self.assigned_object, 'primary_mac_address'): - if self.assigned_object.primary_mac_address and self.assigned_object.primary_mac_address.pk == self.pk: - return True + # Compare against primary_mac_address_id (a column already loaded on the assigned object) rather than + # dereferencing primary_mac_address, to avoid an extra query per object in list responses. + if (obj := self.assigned_object) is not None and hasattr(obj, 'primary_mac_address_id'): + return obj.primary_mac_address_id == self.pk return False def clean(self, *args, **kwargs): diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 42e550a5c..f8e679fc5 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -3880,3 +3880,25 @@ class MACAddressTestCase(APIViewTestCases.APIViewTestCase): 'mac_address': '00:00:00:00:00:06', }, ] + + def test_is_primary_field(self): + """ + The read-only is_primary field should reflect whether the MAC address is the primary on its interface. + """ + self.add_permissions('dcim.view_macaddress') + + primary_mac = MACAddress.objects.get(mac_address='00:00:00:00:00:01') + non_primary_mac = MACAddress.objects.get(mac_address='00:00:00:00:00:02') + + # Designate one MAC address as the primary on its interface + interface = primary_mac.assigned_object + interface.primary_mac_address = primary_mac + interface.save() + + url = reverse('dcim-api:macaddress-detail', kwargs={'pk': primary_mac.pk}) + response = self.client.get(url, **self.header) + self.assertTrue(response.data['is_primary']) + + url = reverse('dcim-api:macaddress-detail', kwargs={'pk': non_primary_mac.pk}) + response = self.client.get(url, **self.header) + self.assertFalse(response.data['is_primary'])