tfranzel / drf-spectacular

Sane and flexible OpenAPI 3 schema generation for Django REST framework.
https://drf-spectacular.readthedocs.io
BSD 3-Clause "New" or "Revised" License
2.22k stars 250 forks source link

Don't make serializer Partial/Patched when using PATCH endpoint #1246

Open HansAarneLiblik opened 1 month ago

HansAarneLiblik commented 1 month ago

Describe the bug I have some API endpoints, that need to be PATCH requests, but they are not conventional. They have at least 1 field, all of which are required

If I provide a serializer for this, drf-spectacular will make it a "PatchedSerializer", by making the serializer partial.

image

To Reproduce

class MySerIn(serializers.Serializer):
    new_profile = serializers.CharField(required=True)

@extend_schema(
    methods=["PATCH"],
    request=MySerIn,
    responses=MySerializerOut,
)
@api_view(["PATCH"])
def cat_change_profile(request: Request, object_id: int) -> Response:
  pass

Generated Schema

  /api/cats/{object_id}/change-profile:
    patch:
      operationId: cat_change_profile_partial_update
      summary: cat_not_dog
      parameters:
        - in: path
          name: object_id
          schema:
            type: integer
          required: true
      tags:
        - cat
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedMySerIn'  # <---- Patched
      security:
        - cookieAuth: []
        - basicAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MySerializerOut'
          description: ''

Component Schema

    PatchedMySerIn:
      type: object
      properties:
        new_profile:
          type: string

Expected behavior I'd like a way to tell drf-spectacular to NOT make this serializer partial, as all the fields are required. Perhaps via

@extend_schema(methods=["PATCH"], request=MySerializerIn, responses=MySerializerOut, force_impartial_serializer=True)

or a custom serializer field

@extend_schema(methods=["PATCH"], request=MySerializerIn(force_impartial=True), responses=MySerializerOut)
tfranzel commented 1 month ago

Hi @HansAarneLiblik,

wouldn't an update endpoint where all fields are required be an PUT operation instead of PATCH? The whole point of PATCH is to allow partial updates. For whole updates we have PUT, right? I feel you struggle here because you try to shoehorn some functionality that is by definition not "restful".

Not sure if we should complicate the implementation, because it is certainly not as easy as MySerializerIn(force_impartial=True), because DRF does not allow extra unknown arguments, which was a route I tried to go before for another feature.

intgr commented 1 day ago

wouldn't an update endpoint where all fields are required be an PUT operation instead of PATCH?

I think there are use cases for required fields in PATCH endpoints. For instance

I think a fair argument could be that these aren't fully RESTful and should use POST method (though there's no official document defining what "RESTful" truly means). But I have used PATCH for such endpoints a couple of times and I don't think it's "too wrong".