encode / django-rest-framework

Web APIs for Django. 🎸
https://www.django-rest-framework.org
Other
28.26k stars 6.82k forks source link

DRF doesn't change the media type of request body to multipart/form-data automatically #6863

Open knivets opened 5 years ago

knivets commented 5 years ago

Checklist

Steps to reproduce

# view.py
from rest_framework import generics
from rest_framework import serializers
from django.db import models

class Book(models.Model):
  title = models.CharField(max_length=200)

class BookSerializer(serializers.ModelSerializer):
  cover = serializers.FileField()
  class Meta:
    model = Book
    fields = ('cover',)

class BooksView(generics.CreateAPIView):
  queryset = Book.objects.all()   
  serializer_class = BookSerializer

# urls.py
urlpatterns = [
...
path('api/books/', views.BooksView.as_view()),
...
]

Expected behavior

./manage.py generateschema 
openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/:
    post:
      operationId: CreateBook
      parameters: []
      requestBody:
        content:
          multipart/form-data:
            schema:
              required:
              - cover
              properties:
                cover:
                  type: string
                  format: binary
      responses:
        '200':
          content:
            application/json:
              schema:
                required:
                - cover
                properties:
                  cover:
                    type: string

Actual behavior

./manage.py generateschema 
openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/books/:
    post:
      operationId: CreateBook
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              required:
              - cover
              properties:
                cover:
                  type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                required:
                - cover
                properties:
                  cover:
                    type: string

There are two things going on here: 1) There is no support for automatic multipart/form-data media type generation 2) The FileField generates incomplete OpenAPI schema: it doesn't include format: binary. Finally, it should only generate format: binary for requests, since you can't have a binary string field within application/json media type. We could probably add a format: uri in case use_url=True.

https://swagger.io/docs/specification/describing-request-body/multipart-requests/

On unrelated note I noticed that DRF doesn't put type: object under schema even though this spec passes validation here

tomchristie commented 5 years ago

Yeah seems valid to me. Of course it’s less obvious what we should do if we have both FileField and composite field types (that multipart can’t support.)

knivets commented 5 years ago

Could you please give an example of request body with file and composite field?

tomchristie commented 5 years ago

Eg. Anything with both FileField and DictField

tomchristie commented 5 years ago

Or FileField and a nested serializer

carltongibson commented 5 years ago

@knivets you fancy taking a pop at this? As Tom says, we should be able to do the simple cases at least.

(There may come a complexity level where we say “use a subclass to specify this particular case” rather than try and solve everything, but we’re not there yet...)

knivets commented 5 years ago

Yes! I’ll work on this over the weekend.

gnuletik commented 4 years ago

@knivets Do you have an idea what is the expected schema using OpenAPI Components ? Component's goal is to keep the same properties for request and response and reference them.

In the example you posted in "Expected behavior", the format: binary property is only in the "requestBody". Should the schema include a different component for request and response?

I wasn't able to find any example about this on the web. Thanks!