Address PR feedback
Some checks failed
CI / build (20.x, 3.10) (push) Has been cancelled
CI / build (20.x, 3.11) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled

- Removed brittle serializer marking mechanism in favor of direct checks
  on behavior.
- Attempted to introduce a bulk_create action and then route to it on
  POST in NetBoxRouter, but ran in to several obstacles including
  breaking HTTP status code reporting in the schema. Opted to simply
This commit is contained in:
Jason Novinger
2025-11-20 03:44:46 -06:00
parent 773d86dd85
commit cb6cbe214d
2 changed files with 7 additions and 31 deletions

View File

@@ -260135,4 +260135,4 @@
"description": "NetBox"
}
]
}
}

View File

@@ -12,6 +12,7 @@ from drf_spectacular.utils import Direction
from netbox.api.fields import ChoiceField
from netbox.api.serializers import WritableNestedSerializer
from netbox.api.viewsets import NetBoxModelViewSet
# see netbox.api.routers.NetBoxRouter
BULK_ACTIONS = ("bulk_destroy", "bulk_partial_update", "bulk_update")
@@ -50,15 +51,8 @@ class ChoiceFieldFix(OpenApiSerializerFieldExtension):
def viewset_handles_bulk_create(view):
"""
Check if viewset explicitly declares bulk create support.
Viewsets opt-in to bulk create by setting bulk_create_enabled = True.
This allows POST operations to accept either single objects or arrays.
Refs: #20638
"""
return getattr(view, 'bulk_create_enabled', False)
"""Check if view automatically provides list-based bulk create"""
return isinstance(view, NetBoxModelViewSet)
class NetBoxAutoSchema(AutoSchema):
@@ -118,36 +112,17 @@ class NetBoxAutoSchema(AutoSchema):
if self.is_bulk_action:
return type(serializer)(many=True)
# For create operations (POST to list endpoints) on viewsets that support
# bulk create, mark the serializer so we can generate oneOf schema.
# The 'create' action is only used for POST to collection endpoints.
# Refs: #20638
if (
getattr(self.view, 'action', None) == 'create' and
viewset_handles_bulk_create(self.view)
):
# Mark this serializer as supporting array input
# This will be used in _get_request_for_media_type()
if serializer is not None:
serializer._netbox_supports_array = True
# handle mapping for Writable serializers - adapted from dansheps original code
# for drf-yasg
if serializer is not None and self.method in WRITABLE_ACTIONS:
writable_class = self.get_writable_class(serializer)
if writable_class is not None:
# Preserve the array support marker when creating writable serializer
supports_array = getattr(serializer, '_netbox_supports_array', False)
if hasattr(serializer, "child"):
child_serializer = self.get_writable_class(serializer.child)
serializer = writable_class(context=serializer.context, child=child_serializer)
else:
serializer = writable_class(context=serializer.context)
if supports_array:
serializer._netbox_supports_array = True
return serializer
def get_response_serializers(self) -> typing.Any:
@@ -159,7 +134,7 @@ class NetBoxAutoSchema(AutoSchema):
return response_serializers
def _get_request_for_media_type(self, serializer, direction):
def _get_request_for_media_type(self, serializer, direction='request'):
"""
Override to generate oneOf schema for serializers that support both
single object and array input (NetBoxModelViewSet POST operations).
@@ -174,7 +149,8 @@ class NetBoxAutoSchema(AutoSchema):
if (
direction == 'request' and
schema is not None and
getattr(serializer, '_netbox_supports_array', False)
getattr(self.view, 'action', None) == 'create' and
viewset_handles_bulk_create(self.view)
):
return {
'oneOf': [