mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Closes #5190: Add a REST API endpoint for content types
This commit is contained in:
parent
66c4597525
commit
3df3706f27
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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']
|
||||
|
@ -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/',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user