django-rest-framework icon indicating copy to clipboard operation
django-rest-framework copied to clipboard

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

Open knivets opened this issue 6 years ago • 7 comments

Checklist

  • [x] I have verified that that issue exists against the master branch of Django REST framework.
  • [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
  • [x] This is not a usage question. (Those should be directed to the discussion group instead.)
  • [x] This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where possible.)
  • [x] I have reduced the issue to the simplest possible case.
  • [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

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

knivets avatar Aug 09 '19 17:08 knivets

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.)

lovelydinosaur avatar Aug 09 '19 17:08 lovelydinosaur

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

knivets avatar Aug 09 '19 18:08 knivets

Eg. Anything with both FileField and DictField

lovelydinosaur avatar Aug 09 '19 21:08 lovelydinosaur

Or FileField and a nested serializer

lovelydinosaur avatar Aug 09 '19 21:08 lovelydinosaur

@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...)

carltongibson avatar Aug 10 '19 14:08 carltongibson

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

knivets avatar Aug 10 '19 14:08 knivets

@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!

gnuletik avatar Feb 11 '20 16:02 gnuletik