Skip to content

Document endpoint supporting both many=True and many=False #692

@CelestialGuru

Description

@CelestialGuru

I have a viewset that currently supports creation of a single or multiple items at once. It looks something like this:

class FooViewSet(viewsets.ModelViewSet):
    def create(self, request, *args, **kwargs):
        if not isinstance(request.data, list):
            return super().create(request, *args, **kwargs)
        else:
            serializer = self.get_serializer(data=request.data, many=True)
            serializer.is_valid(raise_exception=True)
            self.perform_bulk_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

There are two ways this could be documented. Either by reusing the component schema with something like this:

content:
  application/json:
    schema:
      anyOf:
        - $ref: '#/components/schemas/Foo'
        - type: array
          items:
            $ref: '#/components/schemas/Foo'
schema.yaml reusing component schemas
openapi: 3.0.3
info:
  title: ''
  version: 0.0.0
paths:
  /api/foos/foo/:
    post:
      operationId: foo_foo_create
      description: ''
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/FooRequest'
                - type: array
                  items:
                    $ref: '#/components/schemas/FooRequest'
                  
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/Foo'
                  - type: array
                    items:
                      $ref: '#/components/schemas/Foo'
          description: ''
components:
  schemas:
    Foo:
      type: object
      properties:
        id:
          type: integer
        some_field:
          type: integer
      required:
        - id
        - some_field
    FooRequest:
      type: object
      properties:
        some_field:
          type: integer
      required:
        - some_field

Or perhaps one could define multiple component schemas:

content:
  application/json:
    schema:
      anyOf:
        - $ref: '#/components/schemas/Foo'
        - $ref: '#/components/schemas/FooList'
schema.yaml with multiple component schemas
openapi: 3.0.3
info:
  title: ''
  version: 0.0.0
paths:
  /api/foos/foo/:
    post:
      operationId: foo_foo_create
      description: ''
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/FooRequest'
                - $ref: '#/components/schemas/FooRequestList'
                  
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/Foo'
                  - $ref: '#/components/schemas/FooList'
          description: ''
components:
  schemas:
    Foo:
      type: object
      properties:
        id:
          type: integer
        some_field:
          type: integer
      required:
        - id
        - some_field
    FooList:
      type: array
      items:
        $ref: '#/components/schemas/Foo'
    FooRequest:
      type: object
      properties:
        some_field:
          type: integer
      required:
        - some_field
    FooRequestList:
      type: array
      items:
        $ref: '#/components/schemas/FooRequest'

I've tried the PolymorphicProxySerializer but that doesn't seem to work here.

This just generates an empty request:

@extend_schema(
    request=PolymorphicProxySerializer(
        "DifferentRequests",
        serializers=[
            FooSerializer,
            inline_serializer("ListSerializer", fields={"items": FooSerializer()}),
        ],
        resource_type_field_name=None,
    )
)

This just gives an error 'child' is a required argument:

@extend_schema(
    request=PolymorphicProxySerializer(
        "DifferentRequests",
        serializers=[
            FooSerializer,  # FooSerializer.Meta.list_serializer_class == FooListSerializer
            FooListSerializer,  # FooListSerializer isinstance of ListSerializer
        ],
        resource_type_field_name=None,
    )
)

This just fails:

@extend_schema(
    request=PolymorphicProxySerializer(
        "DifferentRequests",
        serializers=[
            FooSerializer,
            FooSerializer(many=True),  # extend_schema expecting type, not an instance
        ],
        resource_type_field_name=None,
    )
)

How can I have drf-spectacular generate the correct documentation for me in this case? I want to have an api that supports both single objects and list of objects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requestfix confirmation pendingissue has been fixed and confirmation from issue reporter is pending

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions