diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4a4f24b..468263ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ v2.4.5 (2018-10-02) ## Bug Fixes +* [#2400](https://github.com/digitalocean/netbox/issues/2400) - API variable type mismatch at creating/modifying an entry * [#2406](https://github.com/digitalocean/netbox/issues/2406) - Remove hard-coded limit of 1000 objects from API-populated form fields * [#2414](https://github.com/digitalocean/netbox/issues/2414) - Tags field missing from device/VM component creation forms * [#2442](https://github.com/digitalocean/netbox/issues/2442) - Nullify "next" link in API when limit=0 is passed diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index f6fbcdf70..7034788fb 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -275,9 +275,12 @@ RQ_QUEUES = { # drf_yasg settings for Swagger SWAGGER_SETTINGS = { + 'DEFAULT_AUTO_SCHEMA_CLASS': 'utilities.custom_inspectors.NetBoxSwaggerAutoSchema', 'DEFAULT_FIELD_INSPECTORS': [ 'utilities.custom_inspectors.NullableBooleanFieldInspector', 'utilities.custom_inspectors.CustomChoiceFieldInspector', + 'utilities.custom_inspectors.TagListFieldInspector', + 'utilities.custom_inspectors.SerializedPKRelatedFieldInspector', 'drf_yasg.inspectors.CamelCaseJSONFilter', 'drf_yasg.inspectors.ReferencingSerializerInspector', 'drf_yasg.inspectors.RelatedFieldInspector', diff --git a/netbox/utilities/custom_inspectors.py b/netbox/utilities/custom_inspectors.py index ca6e08fc1..5975788bc 100644 --- a/netbox/utilities/custom_inspectors.py +++ b/netbox/utilities/custom_inspectors.py @@ -1,9 +1,52 @@ from drf_yasg import openapi -from drf_yasg.inspectors import FieldInspector, NotHandled, PaginatorInspector, FilterInspector +from drf_yasg.inspectors import FieldInspector, NotHandled, PaginatorInspector, FilterInspector, SwaggerAutoSchema from rest_framework.fields import ChoiceField +from rest_framework.relations import ManyRelatedField +from taggit_serializer.serializers import TagListSerializerField from extras.api.customfields import CustomFieldsSerializer -from utilities.api import ChoiceField +from utilities.api import ChoiceField, SerializedPKRelatedField, WritableNestedSerializer + + +class NetBoxSwaggerAutoSchema(SwaggerAutoSchema): + def get_request_serializer(self): + serializer = super().get_request_serializer() + + if serializer is not None and self.method in self.implicit_body_methods: + properties = {} + for child_name, child in serializer.fields.items(): + if isinstance(child, (ChoiceField, WritableNestedSerializer)): + properties[child_name] = None + elif isinstance(child, ManyRelatedField) and isinstance(child.child_relation, SerializedPKRelatedField): + properties[child_name] = None + + if properties: + writable_class = type('Writable' + type(serializer).__name__, (type(serializer),), properties) + serializer = writable_class() + + return serializer + + +class SerializedPKRelatedFieldInspector(FieldInspector): + def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): + SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) + if isinstance(field, SerializedPKRelatedField): + return self.probe_field_inspectors(field.serializer(), ChildSwaggerType, use_references) + + return NotHandled + + +class TagListFieldInspector(FieldInspector): + def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): + SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) + if isinstance(field, TagListSerializerField): + child_schema = self.probe_field_inspectors(field.child, ChildSwaggerType, use_references) + return SwaggerType( + type=openapi.TYPE_ARRAY, + items=child_schema, + ) + + return NotHandled class CustomChoiceFieldInspector(FieldInspector):