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 = routers.DefaultRouter()
 router.APIRootView = CircuitsRootView
 router.APIRootView = CircuitsRootView
 
 
+# Field choices
+router.register(r'_choices', views.CircuitsFieldChoicesViewSet, base_name='field-choice')
+
 # Providers
 # Providers
 router.register(r'providers', views.ProviderViewSet)
 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.models import Graph, GRAPH_TYPE_PROVIDER
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 from . import serializers
 
 
 
 
 #
 #
+# Field choices
+#
+
+class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (CircuitTermination, ['term_side']),
+    )
+
+
+#
 # Providers
 # Providers
 #
 #
 
 

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

@@ -16,6 +16,9 @@ class DCIMRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.APIRootView = DCIMRootView
 router.APIRootView = DCIMRootView
 
 
+# Field choices
+router.register(r'_choices', views.DCIMFieldChoicesViewSet, base_name='field-choice')
+
 # Sites
 # Sites
 router.register(r'regions', views.RegionViewSet)
 router.register(r'regions', views.RegionViewSet)
 router.register(r'sites', views.SiteViewSet)
 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.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
 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 .exceptions import MissingFilterException
 from . import serializers
 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
 # Regions
 #
 #
 
 

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

@@ -16,6 +16,9 @@ class ExtrasRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.APIRootView = ExtrasRootView
 router.APIRootView = ExtrasRootView
 
 
+# Field choices
+router.register(r'_choices', views.ExtrasFieldChoicesViewSet, base_name='field-choice')
+
 # Graphs
 # Graphs
 router.register(r'graphs', views.GraphViewSet)
 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 django.shortcuts import get_object_or_404
 
 
 from extras import filters
 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 extras.reports import get_report, get_reports
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 from . import serializers
 
 
 
 
+#
+# Field choices
+#
+
+class ExtrasFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (CustomField, ['type']),
+        (Graph, ['type']),
+    )
+
+
+#
+# Custom fields
+#
+
 class CustomFieldModelViewSet(ModelViewSet):
 class CustomFieldModelViewSet(ModelViewSet):
     """
     """
     Include the applicable set of CustomFields in the ModelViewSet context.
     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')
         return super(CustomFieldModelViewSet, self).get_queryset().prefetch_related('custom_field_values__field')
 
 
 
 
+#
+# Graphs
+#
+
 class GraphViewSet(WritableSerializerMixin, ModelViewSet):
 class GraphViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = Graph.objects.all()
     queryset = Graph.objects.all()
     serializer_class = serializers.GraphSerializer
     serializer_class = serializers.GraphSerializer
@@ -53,12 +72,20 @@ class GraphViewSet(WritableSerializerMixin, ModelViewSet):
     filter_class = filters.GraphFilter
     filter_class = filters.GraphFilter
 
 
 
 
+#
+# Export templates
+#
+
 class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
 class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
     serializer_class = serializers.ExportTemplateSerializer
     serializer_class = serializers.ExportTemplateSerializer
     filter_class = filters.ExportTemplateFilter
     filter_class = filters.ExportTemplateFilter
 
 
 
 
+#
+# Topology maps
+#
+
 class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
 class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = TopologyMap.objects.select_related('site')
     queryset = TopologyMap.objects.select_related('site')
     serializer_class = serializers.TopologyMapSerializer
     serializer_class = serializers.TopologyMapSerializer
@@ -85,12 +112,20 @@ class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
         return response
         return response
 
 
 
 
+#
+# Image attachments
+#
+
 class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
 class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = ImageAttachment.objects.all()
     queryset = ImageAttachment.objects.all()
     serializer_class = serializers.ImageAttachmentSerializer
     serializer_class = serializers.ImageAttachmentSerializer
     write_serializer_class = serializers.WritableImageAttachmentSerializer
     write_serializer_class = serializers.WritableImageAttachmentSerializer
 
 
 
 
+#
+# Reports
+#
+
 class ReportViewSet(ViewSet):
 class ReportViewSet(ViewSet):
     _ignore_model_permissions = True
     _ignore_model_permissions = True
     exclude_from_schema = True
     exclude_from_schema = True
@@ -162,6 +197,10 @@ class ReportViewSet(ViewSet):
         return Response(serializer.data)
         return Response(serializer.data)
 
 
 
 
+#
+# User activity
+#
+
 class RecentActivityViewSet(ReadOnlyModelViewSet):
 class RecentActivityViewSet(ReadOnlyModelViewSet):
     """
     """
     List all UserActions to provide a log of recent activity.
     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 = routers.DefaultRouter()
 router.APIRootView = IPAMRootView
 router.APIRootView = IPAMRootView
 
 
+# Field choices
+router.register(r'_choices', views.IPAMFieldChoicesViewSet, base_name='field-choice')
+
 # VRFs
 # VRFs
 router.register(r'vrfs', views.VRFViewSet)
 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.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from ipam import filters
 from ipam import filters
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 from . import serializers
 
 
 
 
 #
 #
+# Field choices
+#
+
+class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (Aggregate, ['family']),
+        (Prefix, ['family', 'status']),
+        (IPAddress, ['family', 'status', 'role']),
+        (VLAN, ['status']),
+        (Service, ['protocol']),
+    )
+
+
+#
 # VRFs
 # VRFs
 #
 #
 
 

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

@@ -16,6 +16,9 @@ class SecretsRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.APIRootView = SecretsRootView
 router.APIRootView = SecretsRootView
 
 
+# Field choices
+router.register(r'_choices', views.SecretsFieldChoicesViewSet, base_name='field-choice')
+
 # Secrets
 # Secrets
 router.register(r'secret-roles', views.SecretRoleViewSet)
 router.register(r'secret-roles', views.SecretRoleViewSet)
 router.register(r'secrets', views.SecretViewSet)
 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 import filters
 from secrets.exceptions import InvalidKey
 from secrets.exceptions import InvalidKey
 from secrets.models import Secret, SecretRole, SessionKey, UserKey
 from secrets.models import Secret, SecretRole, SessionKey, UserKey
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 from . import serializers
 
 
 
 
@@ -23,6 +23,14 @@ ERR_PRIVKEY_INVALID = "Invalid private key."
 
 
 
 
 #
 #
+# Field choices
+#
+
+class SecretsFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = ()
+
+
+#
 # Secret Roles
 # Secret Roles
 #
 #
 
 

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

@@ -16,6 +16,9 @@ class TenancyRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.APIRootView = TenancyRootView
 router.APIRootView = TenancyRootView
 
 
+# Field choices
+router.register(r'_choices', views.TenancyFieldChoicesViewSet, base_name='field-choice')
+
 # Tenants
 # Tenants
 router.register(r'tenant-groups', views.TenantGroupViewSet)
 router.register(r'tenant-groups', views.TenantGroupViewSet)
 router.register(r'tenants', views.TenantViewSet)
 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 extras.api.views import CustomFieldModelViewSet
 from tenancy import filters
 from tenancy import filters
 from tenancy.models import Tenant, TenantGroup
 from tenancy.models import Tenant, TenantGroup
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from . import serializers
 from . import serializers
 
 
 
 
 #
 #
+# Field choices
+#
+
+class TenancyFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = ()
+
+
+#
 # Tenant Groups
 # Tenant Groups
 #
 #
 
 

+ 53 - 0
netbox/utilities/api.py

@@ -1,12 +1,16 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
+from collections import OrderedDict
 
 
 from django.conf import settings
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
+from django.http import Http404
 
 
 from rest_framework.compat import is_authenticated
 from rest_framework.compat import is_authenticated
 from rest_framework.exceptions import APIException
 from rest_framework.exceptions import APIException
 from rest_framework.permissions import BasePermission
 from rest_framework.permissions import BasePermission
+from rest_framework.response import Response
 from rest_framework.serializers import Field, ModelSerializer, ValidationError
 from rest_framework.serializers import Field, ModelSerializer, ValidationError
+from rest_framework.viewsets import ViewSet
 
 
 
 
 WRITE_OPERATIONS = ['create', 'update', 'partial_update', 'delete']
 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
 # Mixins
 #
 #
 
 

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

@@ -16,6 +16,9 @@ class VirtualizationRootView(routers.APIRootView):
 router = routers.DefaultRouter()
 router = routers.DefaultRouter()
 router.APIRootView = VirtualizationRootView
 router.APIRootView = VirtualizationRootView
 
 
+# Field choices
+router.register(r'_choices', views.VirtualizationFieldChoicesViewSet, base_name='field-choice')
+
 # Clusters
 # Clusters
 router.register(r'cluster-types', views.ClusterTypeViewSet)
 router.register(r'cluster-types', views.ClusterTypeViewSet)
 router.register(r'cluster-groups', views.ClusterGroupViewSet)
 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 dcim.models import Interface
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
 from virtualization import filters
 from virtualization import filters
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from . import serializers
 from . import serializers
 
 
 
 
 #
 #
+# Field choices
+#
+
+class VirtualizationFieldChoicesViewSet(FieldChoicesViewSet):
+    fields = (
+        (VirtualMachine, ['status']),
+    )
+
+
+#
 # Clusters
 # Clusters
 #
 #