Closes #5190: Add a REST API endpoint for content types

This commit is contained in:
Jeremy Stretch 2020-10-09 15:08:29 -04:00
parent 66c4597525
commit 3df3706f27
7 changed files with 75 additions and 6 deletions

View File

@ -49,6 +49,7 @@ All end-to-end cable paths are now cached using the new CablePath model. This al
* [#4956](https://github.com/netbox-community/netbox/issues/4956) - Include inventory items on primary device view
* [#5003](https://github.com/netbox-community/netbox/issues/5003) - CSV import now accepts slug values for choice fields
* [#5146](https://github.com/netbox-community/netbox/issues/5146) - Add custom fields support for cables, power panels, rack reservations, and virtual chassis
* [#5190](https://github.com/netbox-community/netbox/issues/5190) - Add a REST API endpoint for content types
### Other Changes
@ -62,6 +63,7 @@ All end-to-end cable paths are now cached using the new CablePath model. This al
### REST API Changes
* Added support for `PUT`, `PATCH`, and `DELETE` operations on list endpoints (bulk update and delete)
* Added `/extras/content-types/` endpoint for Django ContentTypes
* circuits.CircuitTermination:
* Added the `/trace/` endpoint
* Replaced `connection_status` with `connected_endpoint_reachable` (boolean)

View File

@ -339,3 +339,20 @@ class ObjectChangeSerializer(serializers.ModelSerializer):
data = serializer(obj.changed_object, context=context).data
return data
#
# ContentTypes
#
class ContentTypeSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:contenttype-detail')
display_name = serializers.SerializerMethodField()
class Meta:
model = ContentType
fields = ['id', 'url', 'app_label', 'model', 'display_name']
@swagger_serializer_method(serializer_or_field=serializers.CharField)
def get_display_name(self, obj):
return obj.app_labeled_name

View File

@ -29,5 +29,8 @@ router.register('object-changes', views.ObjectChangeViewSet)
# Job Results
router.register('job-results', views.JobResultViewSet)
# ContentTypes
router.register('content-types', views.ContentTypeViewSet)
app_name = 'extras-api'
urlpatterns = router.urls

View File

@ -1,5 +1,3 @@
from collections import OrderedDict
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count
from django.http import Http404
@ -311,3 +309,18 @@ class JobResultViewSet(ReadOnlyModelViewSet):
queryset = JobResult.objects.prefetch_related('user')
serializer_class = serializers.JobResultSerializer
filterset_class = filters.JobResultFilterSet
#
# ContentTypes
#
class ContentTypeViewSet(ReadOnlyModelViewSet):
"""
Read-only list of ContentTypes. Limit results to ContentTypes pertinent to NetBox objects.
"""
queryset = ContentType.objects.order_by('app_label', 'model').filter(app_label__in=(
'circuits', 'dcim', 'extras', 'ipam', 'secrets', 'tenancy', 'users', 'virtualization'
))
serializer_class = serializers.ContentTypeSerializer
filterset_class = filters.ContentTypeFilterSet

View File

@ -13,6 +13,7 @@ from .models import ConfigContext, CustomField, ExportTemplate, ImageAttachment,
__all__ = (
'ConfigContextFilterSet',
'ContentTypeFilterSet',
'CreatedUpdatedFilterSet',
'CustomFieldFilter',
'CustomFieldFilterSet',
@ -313,3 +314,14 @@ class JobResultFilterSet(BaseFilterSet):
return queryset.filter(
Q(user__username__icontains=value)
)
#
# ContentTypes
#
class ContentTypeFilterSet(django_filters.FilterSet):
class Meta:
model = ContentType
fields = ['id', 'app_label', 'model']

View File

@ -362,11 +362,14 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
api_url='/api/users/users/',
)
)
changed_object_type_id = forms.ModelChoiceField(
queryset=ContentType.objects.order_by('app_label', 'model'),
changed_object_type_id = DynamicModelMultipleChoiceField(
queryset=ContentType.objects.all(),
required=False,
widget=ContentTypeSelect(),
label='Object Type'
display_field='display_name',
label='Object Type',
widget=APISelectMultiple(
api_url='/api/extras/content-types/',
)
)

View File

@ -2,6 +2,7 @@ import datetime
from unittest import skipIf
from django.contrib.contenttypes.models import ContentType
from django.test import override_settings
from django.urls import reverse
from django.utils import timezone
from django_rq.queues import get_connection
@ -396,3 +397,21 @@ class CreatedUpdatedFilterTest(APITestCase):
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['id'], self.rack2.pk)
class ContentTypeTest(APITestCase):
@override_settings(EXEMPT_VIEW_PERMISSIONS=['contenttypes.contenttype'])
def test_list_objects(self):
contenttype_count = ContentType.objects.count()
response = self.client.get(reverse('extras-api:contenttype-list'), **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(response.data['count'], contenttype_count)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['contenttypes.contenttype'])
def test_get_object(self):
contenttype = ContentType.objects.first()
url = reverse('extras-api:contenttype-detail', kwargs={'pk': contenttype.pk})
self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_200_OK)