1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Add REST API endpoint for custom fields

This commit is contained in:
Jeremy Stretch
2020-11-23 15:25:20 -05:00
parent 6fb080ff4c
commit a05fe69043
6 changed files with 61 additions and 12 deletions

View File

@ -6,6 +6,7 @@ from users.api.nested_serializers import NestedUserSerializer
__all__ = [ __all__ = [
'NestedConfigContextSerializer', 'NestedConfigContextSerializer',
'NestedCustomFieldSerializer',
'NestedExportTemplateSerializer', 'NestedExportTemplateSerializer',
'NestedImageAttachmentSerializer', 'NestedImageAttachmentSerializer',
'NestedJobResultSerializer', 'NestedJobResultSerializer',
@ -13,6 +14,14 @@ __all__ = [
] ]
class NestedCustomFieldSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
class Meta:
model = models.CustomField
fields = ['id', 'url', 'name']
class NestedConfigContextSerializer(WritableNestedSerializer): class NestedConfigContextSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail') url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail')

View File

@ -10,7 +10,7 @@ from dcim.api.nested_serializers import (
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
from extras.choices import * from extras.choices import *
from extras.models import ( from extras.models import (
ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag, ConfigContext, CustomField, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag,
) )
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer
@ -24,6 +24,27 @@ from virtualization.models import Cluster, ClusterGroup
from .nested_serializers import * from .nested_serializers import *
#
# Custom fields
#
class CustomFieldSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.filter(FeatureQuery('export_templates').get_query()),
many=True
)
type = ChoiceField(choices=CustomFieldTypeChoices)
filter_logic = ChoiceField(choices=CustomFieldFilterLogicChoices, required=False)
class Meta:
model = CustomField
fields = [
'id', 'url', 'content_types', 'type', 'name', 'label', 'description', 'required', 'filter_logic',
'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices',
]
# #
# Export templates # Export templates
# #

View File

@ -5,6 +5,9 @@ from . import views
router = OrderedDefaultRouter() router = OrderedDefaultRouter()
router.APIRootView = views.ExtrasRootView router.APIRootView = views.ExtrasRootView
# Custom fields
router.register('custom-fields', views.CustomFieldViewSet)
# Export templates # Export templates
router.register('export-templates', views.ExportTemplateViewSet) router.register('export-templates', views.ExportTemplateViewSet)

View File

@ -12,17 +12,26 @@ from rq import Worker
from extras import filters from extras import filters
from extras.choices import JobResultStatusChoices from extras.choices import JobResultStatusChoices
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag from extras.models import ConfigContext, CustomField, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag
from extras.reports import get_report, get_reports, run_report from extras.reports import get_report, get_reports, run_report
from extras.scripts import get_script, get_scripts, run_script from extras.scripts import get_script, get_scripts, run_script
from netbox.api.views import ModelViewSet from netbox.api.views import ModelViewSet
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.metadata import ContentTypeMetadata from netbox.api.metadata import ContentTypeMetadata
from utilities.exceptions import RQWorkerNotRunningException from utilities.exceptions import RQWorkerNotRunningException
from utilities.querysets import RestrictedQuerySet
from utilities.utils import copy_safe_request from utilities.utils import copy_safe_request
from . import serializers from . import serializers
class ExtrasRootView(APIRootView):
"""
Extras API root view
"""
def get_view_name(self):
return 'Extras'
class ConfigContextQuerySetMixin: class ConfigContextQuerySetMixin:
""" """
Used by views that work with config context models (device and virtual machine). Used by views that work with config context models (device and virtual machine).
@ -46,18 +55,17 @@ class ConfigContextQuerySetMixin:
return self.queryset.annotate_config_context_data() return self.queryset.annotate_config_context_data()
class ExtrasRootView(APIRootView):
"""
Extras API root view
"""
def get_view_name(self):
return 'Extras'
# #
# Custom fields # Custom fields
# #
class CustomFieldViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = CustomField.objects.all()
serializer_class = serializers.CustomFieldSerializer
filterset_class = filters.CustomFieldFilterSet
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.

View File

@ -74,6 +74,13 @@ class CustomFieldModelFilterSet(django_filters.FilterSet):
self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf) self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf)
class CustomFieldFilterSet(django_filters.FilterSet):
class Meta:
model = CustomField
fields = ['id', 'content_types', 'name', 'required', 'filter_logic', 'default', 'weight']
class ExportTemplateFilterSet(BaseFilterSet): class ExportTemplateFilterSet(BaseFilterSet):
class Meta: class Meta:

View File

@ -13,6 +13,7 @@ from django.utils.safestring import mark_safe
from extras.choices import * from extras.choices import *
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice
from utilities.querysets import RestrictedQuerySet
from utilities.validators import validate_regex from utilities.validators import validate_regex
@ -63,7 +64,7 @@ class CustomFieldModel(models.Model):
raise ValidationError(f"Missing required custom field '{cf.name}'.") raise ValidationError(f"Missing required custom field '{cf.name}'.")
class CustomFieldManager(models.Manager): class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
use_in_migrations = True use_in_migrations = True
def get_for_model(self, model): def get_for_model(self, model):
@ -193,7 +194,7 @@ class CustomField(models.Model):
}) })
# A selection field must have at least two choices defined # A selection field must have at least two choices defined
if self.type == CustomFieldTypeChoices.TYPE_SELECT and len(self.choices) < 2: if self.type == CustomFieldTypeChoices.TYPE_SELECT and self.choices and len(self.choices) < 2:
raise ValidationError({ raise ValidationError({
'choices': "Selection fields must specify at least two choices." 'choices': "Selection fields must specify at least two choices."
}) })