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__ = [
'NestedConfigContextSerializer',
'NestedCustomFieldSerializer',
'NestedExportTemplateSerializer',
'NestedImageAttachmentSerializer',
'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):
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 extras.choices import *
from extras.models import (
ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag,
ConfigContext, CustomField, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag,
)
from extras.utils import FeatureQuery
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer
@ -24,6 +24,27 @@ from virtualization.models import Cluster, ClusterGroup
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
#

View File

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

View File

@ -12,17 +12,26 @@ from rq import Worker
from extras import filters
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.scripts import get_script, get_scripts, run_script
from netbox.api.views import ModelViewSet
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.metadata import ContentTypeMetadata
from utilities.exceptions import RQWorkerNotRunningException
from utilities.querysets import RestrictedQuerySet
from utilities.utils import copy_safe_request
from . import serializers
class ExtrasRootView(APIRootView):
"""
Extras API root view
"""
def get_view_name(self):
return 'Extras'
class ConfigContextQuerySetMixin:
"""
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()
class ExtrasRootView(APIRootView):
"""
Extras API root view
"""
def get_view_name(self):
return 'Extras'
#
# Custom fields
#
class CustomFieldViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = CustomField.objects.all()
serializer_class = serializers.CustomFieldSerializer
filterset_class = filters.CustomFieldFilterSet
class CustomFieldModelViewSet(ModelViewSet):
"""
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)
class CustomFieldFilterSet(django_filters.FilterSet):
class Meta:
model = CustomField
fields = ['id', 'content_types', 'name', 'required', 'filter_logic', 'default', 'weight']
class ExportTemplateFilterSet(BaseFilterSet):
class Meta:

View File

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