diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index 9a6d4d726..d1c778f65 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -1,4 +1,5 @@ from .serializers_.change_logging import * from .serializers_.data import * from .serializers_.jobs import * +from .serializers_.object_types import * from .serializers_.tasks import * diff --git a/netbox/extras/api/serializers_/objecttypes.py b/netbox/core/api/serializers_/object_types.py similarity index 100% rename from netbox/extras/api/serializers_/objecttypes.py rename to netbox/core/api/serializers_/object_types.py diff --git a/netbox/core/api/urls.py b/netbox/core/api/urls.py index 3c22f1cf4..85d07bfa0 100644 --- a/netbox/core/api/urls.py +++ b/netbox/core/api/urls.py @@ -10,6 +10,7 @@ router.register('data-sources', views.DataSourceViewSet) router.register('data-files', views.DataFileViewSet) router.register('jobs', views.JobViewSet) router.register('object-changes', views.ObjectChangeViewSet) +router.register('object-types', views.ObjectTypeViewSet) router.register('background-queues', views.BackgroundQueueViewSet, basename='rqqueue') router.register('background-workers', views.BackgroundWorkerViewSet, basename='rqworker') router.register('background-tasks', views.BackgroundTaskViewSet, basename='rqtask') diff --git a/netbox/core/api/views.py b/netbox/core/api/views.py index 4e5b148fc..e1ae5ed1a 100644 --- a/netbox/core/api/views.py +++ b/netbox/core/api/views.py @@ -17,6 +17,7 @@ from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_jo from django_rq.queues import get_redis_connection from django_rq.utils import get_statistics from django_rq.settings import QUEUES_LIST +from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.metadata import ContentTypeMetadata from netbox.api.pagination import LimitOffsetListPagination from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet @@ -85,6 +86,16 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet): filterset_class = filtersets.ObjectChangeFilterSet +class ObjectTypeViewSet(ReadOnlyModelViewSet): + """ + Read-only list of ObjectTypes. + """ + permission_classes = [IsAuthenticatedOrLoginNotRequired] + queryset = ObjectType.objects.order_by('app_label', 'model') + serializer_class = serializers.ObjectTypeSerializer + filterset_class = filtersets.ObjectTypeFilterSet + + class BaseRQViewSet(viewsets.ViewSet): """ Base class for RQ view sets. Provides a list() method. Subclasses must implement get_data(). diff --git a/netbox/core/filtersets.py b/netbox/core/filtersets.py index 42ec22350..c64bb03ff 100644 --- a/netbox/core/filtersets.py +++ b/netbox/core/filtersets.py @@ -1,9 +1,8 @@ +import django_filters from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.translation import gettext as _ -import django_filters - from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet from netbox.utils import get_data_backend_choices from users.models import User @@ -17,6 +16,7 @@ __all__ = ( 'DataSourceFilterSet', 'JobFilterSet', 'ObjectChangeFilterSet', + 'ObjectTypeFilterSet', ) @@ -134,6 +134,25 @@ class JobFilterSet(BaseFilterSet): ) +class ObjectTypeFilterSet(django_filters.FilterSet): + q = django_filters.CharFilter( + method='search', + label=_('Search'), + ) + + class Meta: + model = ObjectType + fields = ('id', 'app_label', 'model') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(app_label__icontains=value) | + Q(model__icontains=value) + ) + + class ObjectChangeFilterSet(BaseFilterSet): q = django_filters.CharFilter( method='search', diff --git a/netbox/core/tests/test_api.py b/netbox/core/tests/test_api.py index e9e77f252..4a285bdb4 100644 --- a/netbox/core/tests/test_api.py +++ b/netbox/core/tests/test_api.py @@ -7,6 +7,7 @@ from django.utils import timezone from rq.job import Job as RQ_Job, JobStatus from rq.registry import FailedJobRegistry, StartedJobRegistry +from rest_framework import status from users.models import Token, User from utilities.testing import APITestCase, APIViewTestCases, TestCase from utilities.testing.utils import disable_logging @@ -101,6 +102,22 @@ class DataFileTest( DataFile.objects.bulk_create(data_files) +class ObjectTypeTest(APITestCase): + + def test_list_objects(self): + object_type_count = ObjectType.objects.count() + + response = self.client.get(reverse('extras-api:objecttype-list'), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data['count'], object_type_count) + + def test_get_object(self): + object_type = ObjectType.objects.first() + + url = reverse('extras-api:objecttype-detail', kwargs={'pk': object_type.pk}) + self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK) + + class BackgroundTaskTestCase(TestCase): user_permissions = () diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 07540c50d..eb8d050cd 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -1,4 +1,3 @@ -from .serializers_.objecttypes import * from .serializers_.attachments import * from .serializers_.bookmarks import * from .serializers_.customfields import * diff --git a/netbox/extras/api/urls.py b/netbox/extras/api/urls.py index 101808753..bd4a60f6e 100644 --- a/netbox/extras/api/urls.py +++ b/netbox/extras/api/urls.py @@ -1,5 +1,6 @@ from django.urls import include, path +from core.api.views import ObjectTypeViewSet from netbox.api.routers import NetBoxRouter from . import views @@ -26,7 +27,9 @@ router.register('journal-entries', views.JournalEntryViewSet) router.register('config-contexts', views.ConfigContextViewSet) router.register('config-templates', views.ConfigTemplateViewSet) router.register('scripts', views.ScriptViewSet, basename='script') -router.register('object-types', views.ObjectTypeViewSet) + +# TODO: Remove in NetBox v4.5 +router.register('object-types', ObjectTypeViewSet) app_name = 'extras-api' urlpatterns = [ diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 3f5bb172a..4b2208364 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -10,10 +10,9 @@ from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.routers import APIRootView -from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet +from rest_framework.viewsets import ModelViewSet from rq import Worker -from core.models import ObjectType from extras import filtersets from extras.jobs import ScriptJob from extras.models import * @@ -314,20 +313,6 @@ class ScriptViewSet(ModelViewSet): return Response(input_serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# -# Object types -# - -class ObjectTypeViewSet(ReadOnlyModelViewSet): - """ - Read-only list of ObjectTypes. - """ - permission_classes = [IsAuthenticatedOrLoginNotRequired] - queryset = ObjectType.objects.order_by('app_label', 'model') - serializer_class = serializers.ObjectTypeSerializer - filterset_class = filtersets.ObjectTypeFilterSet - - # # User dashboard # diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 6adad110d..6fa81f8d3 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -29,7 +29,6 @@ __all__ = ( 'JournalEntryFilterSet', 'LocalConfigContextFilterSet', 'NotificationGroupFilterSet', - 'ObjectTypeFilterSet', 'SavedFilterFilterSet', 'ScriptFilterSet', 'TableConfigFilterSet', @@ -788,26 +787,3 @@ class LocalConfigContextFilterSet(django_filters.FilterSet): def _local_context_data(self, queryset, name, value): return queryset.exclude(local_context_data__isnull=value) - - -# -# ContentTypes -# - -class ObjectTypeFilterSet(django_filters.FilterSet): - q = django_filters.CharFilter( - method='search', - label=_('Search'), - ) - - class Meta: - model = ObjectType - fields = ('id', 'app_label', 'model') - - def search(self, queryset, name, value): - if not value.strip(): - return queryset - return queryset.filter( - Q(app_label__icontains=value) | - Q(model__icontains=value) - ) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 29af3f96d..16e95703e 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -3,7 +3,6 @@ import datetime from django.contrib.contenttypes.models import ContentType from django.urls import reverse from django.utils.timezone import make_aware, now -from rest_framework import status from core.choices import ManagedFileRootPathChoices from core.events import * @@ -921,22 +920,6 @@ class CreatedUpdatedFilterTest(APITestCase): self.assertEqual(response.data['results'][0]['id'], rack2.pk) -class ObjectTypeTest(APITestCase): - - def test_list_objects(self): - object_type_count = ObjectType.objects.count() - - response = self.client.get(reverse('extras-api:objecttype-list'), **self.header) - self.assertHttpStatus(response, status.HTTP_200_OK) - self.assertEqual(response.data['count'], object_type_count) - - def test_get_object(self): - object_type = ObjectType.objects.first() - - url = reverse('extras-api:objecttype-detail', kwargs={'pk': object_type.pk}) - self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK) - - class SubscriptionTest(APIViewTestCases.APIViewTestCase): model = Subscription brief_fields = ['display', 'id', 'object_id', 'object_type', 'url', 'user']