Browse Source

Closes #1556: Added API endpoints listing static field choices for each app

Jeremy Stretch 7 years ago
parent
commit
f824d1eb3b

+ 3 - 0
netbox/circuits/api/urls.py

@@ -16,6 +16,9 @@ class CircuitsRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = CircuitsRootView
 
+# Field choices
+router.register(r'_choices', views.CircuitsFieldChoicesViewSet, base_name='field-choice')
+
 # Providers
 router.register(r'providers', views.ProviderViewSet)
 

+ 11 - 1
netbox/circuits/api/views.py

@@ -11,11 +11,21 @@ from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
 from extras.models import Graph, GRAPH_TYPE_PROVIDER
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 
 
 #
+# Field choices
+#
+
+class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (CircuitTermination, ['term_side']),
+    )
+
+
+#
 # Providers
 #
 

+ 3 - 0
netbox/dcim/api/urls.py

@@ -16,6 +16,9 @@ class DCIMRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = DCIMRootView
 
+# Field choices
+router.register(r'_choices', views.DCIMFieldChoicesViewSet, base_name='field-choice')
+
 # Sites
 router.register(r'regions', views.RegionViewSet)
 router.register(r'sites', views.SiteViewSet)

+ 17 - 1
netbox/dcim/api/views.py

@@ -20,12 +20,28 @@ from dcim import filters
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
-from utilities.api import IsAuthenticatedOrLoginNotRequired, ServiceUnavailable, WritableSerializerMixin
+from utilities.api import IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ServiceUnavailable, WritableSerializerMixin
 from .exceptions import MissingFilterException
 from . import serializers
 
 
 #
+# Field choices
+#
+
+class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (Device, ['face', 'status']),
+        (ConsolePort, ['connection_status']),
+        (Interface, ['form_factor']),
+        (InterfaceConnection, ['connection_status']),
+        (InterfaceTemplate, ['form_factor']),
+        (PowerPort, ['connection_status']),
+        (Rack, ['type', 'width']),
+    )
+
+
+#
 # Regions
 #
 

+ 3 - 0
netbox/extras/api/urls.py

@@ -16,6 +16,9 @@ class ExtrasRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = ExtrasRootView
 
+# Field choices
+router.register(r'_choices', views.ExtrasFieldChoicesViewSet, base_name='field-choice')
+
 # Graphs
 router.register(r'graphs', views.GraphViewSet)
 

+ 41 - 2
netbox/extras/api/views.py

@@ -10,12 +10,27 @@ from django.http import Http404, HttpResponse
 from django.shortcuts import get_object_or_404
 
 from extras import filters
-from extras.models import ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
+from extras.models import CustomField, ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
 from extras.reports import get_report, get_reports
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 
 
+#
+# Field choices
+#
+
+class ExtrasFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (CustomField, ['type']),
+        (Graph, ['type']),
+    )
+
+
+#
+# Custom fields
+#
+
 class CustomFieldModelViewSet(ModelViewSet):
     """
     Include the applicable set of CustomFields in the ModelViewSet context.
@@ -46,6 +61,10 @@ class CustomFieldModelViewSet(ModelViewSet):
         return super(CustomFieldModelViewSet, self).get_queryset().prefetch_related('custom_field_values__field')
 
 
+#
+# Graphs
+#
+
 class GraphViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = Graph.objects.all()
     serializer_class = serializers.GraphSerializer
@@ -53,12 +72,20 @@ class GraphViewSet(WritableSerializerMixin, ModelViewSet):
     filter_class = filters.GraphFilter
 
 
+#
+# Export templates
+#
+
 class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = ExportTemplate.objects.all()
     serializer_class = serializers.ExportTemplateSerializer
     filter_class = filters.ExportTemplateFilter
 
 
+#
+# Topology maps
+#
+
 class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = TopologyMap.objects.select_related('site')
     serializer_class = serializers.TopologyMapSerializer
@@ -85,12 +112,20 @@ class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
         return response
 
 
+#
+# Image attachments
+#
+
 class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = ImageAttachment.objects.all()
     serializer_class = serializers.ImageAttachmentSerializer
     write_serializer_class = serializers.WritableImageAttachmentSerializer
 
 
+#
+# Reports
+#
+
 class ReportViewSet(ViewSet):
     _ignore_model_permissions = True
     exclude_from_schema = True
@@ -162,6 +197,10 @@ class ReportViewSet(ViewSet):
         return Response(serializer.data)
 
 
+#
+# User activity
+#
+
 class RecentActivityViewSet(ReadOnlyModelViewSet):
     """
     List all UserActions to provide a log of recent activity.

+ 3 - 0
netbox/ipam/api/urls.py

@@ -16,6 +16,9 @@ class IPAMRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = IPAMRootView
 
+# Field choices
+router.register(r'_choices', views.IPAMFieldChoicesViewSet, base_name='field-choice')
+
 # VRFs
 router.register(r'vrfs', views.VRFViewSet)
 

+ 15 - 1
netbox/ipam/api/views.py

@@ -12,11 +12,25 @@ from django.shortcuts import get_object_or_404
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from ipam import filters
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 
 
 #
+# Field choices
+#
+
+class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (Aggregate, ['family']),
+        (Prefix, ['family', 'status']),
+        (IPAddress, ['family', 'status', 'role']),
+        (VLAN, ['status']),
+        (Service, ['protocol']),
+    )
+
+
+#
 # VRFs
 #
 

+ 3 - 0
netbox/secrets/api/urls.py

@@ -16,6 +16,9 @@ class SecretsRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = SecretsRootView
 
+# Field choices
+router.register(r'_choices', views.SecretsFieldChoicesViewSet, base_name='field-choice')
+
 # Secrets
 router.register(r'secret-roles', views.SecretRoleViewSet)
 router.register(r'secrets', views.SecretViewSet)

+ 9 - 1
netbox/secrets/api/views.py

@@ -12,7 +12,7 @@ from django.http import HttpResponseBadRequest
 from secrets import filters
 from secrets.exceptions import InvalidKey
 from secrets.models import Secret, SecretRole, SessionKey, UserKey
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 
 
@@ -23,6 +23,14 @@ ERR_PRIVKEY_INVALID = "Invalid private key."
 
 
 #
+# Field choices
+#
+
+class SecretsFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = ()
+
+
+#
 # Secret Roles
 #
 

+ 3 - 0
netbox/tenancy/api/urls.py

@@ -16,6 +16,9 @@ class TenancyRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = TenancyRootView
 
+# Field choices
+router.register(r'_choices', views.TenancyFieldChoicesViewSet, base_name='field-choice')
+
 # Tenants
 router.register(r'tenant-groups', views.TenantGroupViewSet)
 router.register(r'tenants', views.TenantViewSet)

+ 9 - 1
netbox/tenancy/api/views.py

@@ -5,11 +5,19 @@ from rest_framework.viewsets import ModelViewSet
 from extras.api.views import CustomFieldModelViewSet
 from tenancy import filters
 from tenancy.models import Tenant, TenantGroup
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 
 
 #
+# Field choices
+#
+
+class TenancyFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = ()
+
+
+#
 # Tenant Groups
 #
 

+ 53 - 0
netbox/utilities/api.py

@@ -1,12 +1,16 @@
 from __future__ import unicode_literals
+from collections import OrderedDict
 
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
+from django.http import Http404
 
 from rest_framework.compat import is_authenticated
 from rest_framework.exceptions import APIException
 from rest_framework.permissions import BasePermission
+from rest_framework.response import Response
 from rest_framework.serializers import Field, ModelSerializer, ValidationError
+from rest_framework.viewsets import ViewSet
 
 
 WRITE_OPERATIONS = ['create', 'update', 'partial_update', 'delete']
@@ -95,6 +99,55 @@ class ContentTypeFieldSerializer(Field):
 
 
 #
+# Views
+#
+
+class FieldChoicesViewSet(ViewSet):
+    """
+    Expose the built-in numeric values which represent static choices for a model's field.
+    """
+    permission_classes = [IsAuthenticatedOrLoginNotRequired]
+    fields = []
+
+    def __init__(self, *args, **kwargs):
+        super(FieldChoicesViewSet, self).__init__(*args, **kwargs)
+
+        # Compile a dict of all fields in this view
+        self._fields = OrderedDict()
+        for cls, field_list in self.fields:
+            for field_name in field_list:
+                model_name = cls._meta.verbose_name.lower().replace(' ', '-')
+                key = ':'.join([model_name, field_name])
+                choices = []
+                for k, v in cls._meta.get_field(field_name).choices:
+                    if type(v) in [list, tuple]:
+                        for k2, v2 in v:
+                            choices.append({
+                                'value': v2,
+                                'label': k2,
+                            })
+                    else:
+                        choices.append({
+                            'value': v,
+                            'label': k,
+                        })
+                self._fields[key] = choices
+
+    def list(self, request):
+        return Response(self._fields)
+
+    def retrieve(self, request, pk):
+
+        if pk not in self._fields:
+            raise Http404
+
+        return Response(self._fields[pk])
+
+    def get_view_name(self):
+        return "Field Choices"
+
+
+#
 # Mixins
 #
 

+ 3 - 0
netbox/virtualization/api/urls.py

@@ -16,6 +16,9 @@ class VirtualizationRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router.APIRootView = VirtualizationRootView
 
+# Field choices
+router.register(r'_choices', views.VirtualizationFieldChoicesViewSet, base_name='field-choice')
+
 # Clusters
 router.register(r'cluster-types', views.ClusterTypeViewSet)
 router.register(r'cluster-groups', views.ClusterGroupViewSet)

+ 11 - 1
netbox/virtualization/api/views.py

@@ -4,13 +4,23 @@ from rest_framework.viewsets import ModelViewSet
 
 from dcim.models import Interface
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from virtualization import filters
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from . import serializers
 
 
 #
+# Field choices
+#
+
+class VirtualizationFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (VirtualMachine, ['status']),
+    )
+
+
+#
 # Clusters
 #