From 3f475e0b8df36a84681f2b0c2cd1c2ad0512bccb Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 14 Aug 2025 11:41:43 -0400 Subject: [PATCH] #19924: Expose public & features fields in API serializer and enable filtering --- netbox/core/api/serializers_/object_types.py | 5 ++- netbox/core/filtersets.py | 10 ++++- netbox/core/tests/test_filtersets.py | 45 ++++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/netbox/core/api/serializers_/object_types.py b/netbox/core/api/serializers_/object_types.py index 5ab732e28..c36796b5f 100644 --- a/netbox/core/api/serializers_/object_types.py +++ b/netbox/core/api/serializers_/object_types.py @@ -26,9 +26,10 @@ class ObjectTypeSerializer(BaseModelSerializer): class Meta: model = ObjectType fields = [ - 'id', 'url', 'display', 'app_label', 'app_name', 'model', 'model_name', 'model_name_plural', - 'is_plugin_model', 'rest_api_endpoint', 'description', + 'id', 'url', 'display', 'app_label', 'app_name', 'model', 'model_name', 'model_name_plural', 'public', + 'features', 'is_plugin_model', 'rest_api_endpoint', 'description', ] + read_only_fields = ['public', 'features'] @extend_schema_field(OpenApiTypes.STR) def get_rest_api_endpoint(self, obj): diff --git a/netbox/core/filtersets.py b/netbox/core/filtersets.py index 9f90752d7..215745e7d 100644 --- a/netbox/core/filtersets.py +++ b/netbox/core/filtersets.py @@ -134,15 +134,18 @@ class JobFilterSet(BaseFilterSet): ) -class ObjectTypeFilterSet(django_filters.FilterSet): +class ObjectTypeFilterSet(BaseFilterSet): q = django_filters.CharFilter( method='search', label=_('Search'), ) + features = django_filters.CharFilter( + method='filter_features' + ) class Meta: model = ObjectType - fields = ('id', 'app_label', 'model') + fields = ('id', 'app_label', 'model', 'public') def search(self, queryset, name, value): if not value.strip(): @@ -152,6 +155,9 @@ class ObjectTypeFilterSet(django_filters.FilterSet): Q(model__icontains=value) ) + def filter_features(self, queryset, name, value): + return queryset.filter(features__icontains=value) + class ObjectChangeFilterSet(BaseFilterSet): q = django_filters.CharFilter( diff --git a/netbox/core/tests/test_filtersets.py b/netbox/core/tests/test_filtersets.py index 4b2cff84d..50441cf62 100644 --- a/netbox/core/tests/test_filtersets.py +++ b/netbox/core/tests/test_filtersets.py @@ -241,3 +241,48 @@ class ObjectChangeTestCase(TestCase, BaseFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) params = {'changed_object_type_id': [ContentType.objects.get(app_label='dcim', model='site').pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + + +class ObjectTypeTestCase(TestCase, BaseFilterSetTests): + queryset = ObjectType.objects.all() + filterset = ObjectTypeFilterSet + ignore_fields = ( + 'custom_fields', + 'custom_links', + 'event_rules', + 'export_templates', + 'object_permissions', + 'saved_filters', + ) + + def test_q(self): + params = {'q': 'vrf'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_app_label(self): + self.assertEqual( + self.filterset({'app_label': ['dcim']}, self.queryset).qs.count(), + ObjectType.objects.filter(app_label='dcim').count(), + ) + + def test_model(self): + self.assertEqual( + self.filterset({'model': ['site']}, self.queryset).qs.count(), + ObjectType.objects.filter(model='site').count(), + ) + + def test_public(self): + self.assertEqual( + self.filterset({'public': True}, self.queryset).qs.count(), + ObjectType.objects.filter(public=True).count(), + ) + self.assertEqual( + self.filterset({'public': False}, self.queryset).qs.count(), + ObjectType.objects.filter(public=False).count(), + ) + + def test_feature(self): + self.assertEqual( + self.filterset({'features': 'tags'}, self.queryset).qs.count(), + ObjectType.objects.filter(features__contains=['tags']).count(), + )