Using FileField/ImageField with Swagger UI and drf-spectacular
Asked Answered
K

1

15

I have a Django REST Framework project which uses a ModelViewSet to create APIs for a model containing a FileField.

I've shared a full example of a Django project demonstrating this issue here. But to summarise the key components:

models.py

from django.db import models


class Profile(models.Model):
    image = models.FileField(upload_to='uploads/%Y/%m/%d/')

views.py

from rest_framework import (
    viewsets,
    serializers,
    parsers,
)

from sample import models


class ProfileSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Profile
        fields = ['id', 'image']
        read_only_fields = ['id']


class ProfileViewSet(viewsets.ModelViewSet):
    serializer_class = ProfileSerializer
    queryset = models.Profile.objects.all()

urls.py

from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularSwaggerView,
)

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings

from rest_framework.routers import DefaultRouter

from sample import views


router = DefaultRouter()
router.register('profile', views.ProfileViewSet)


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/schema/', SpectacularAPIView.as_view(), name='api-schema'),
    path(
        'api/docs/',
        SpectacularSwaggerView.as_view(url_name='api-schema'),
        name='api-docs',
    ),
    path('', include(router.urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

settings.py (REST_FRAMEWORK configuration only):

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

requirements.txt

Django==3.2.3
djangorestframework==3.12.4
drf-spectacular==0.16.0

I am generating browsable Swagger-UI docs using drf-spectacular to document the API.

The problem is that the FileField input in Swagger-UI is a string input, and doesn't give the option to set a file:

enter image description here

I would like to have a file input where I can choose a file which will be sent to the API.

My question is: How can I configure DRF or drf-spectacular to show this field?

Kinswoman answered 13/5, 2021 at 15:11 Comment(0)
K
29

After some digging through the drf-spectacular docs and GitHub issues, I found that the FileField can be set to binary by adding the following to in settings.py:

SPECTACULAR_SETTINGS = {
    'COMPONENT_SPLIT_REQUEST': True
}

Also in Swagger UI, make sure you change the content-type from application/json to multipart/form-data and click Try it out. The upload button will apppear.

Kinswoman answered 13/5, 2021 at 15:53 Comment(5)
@JPG thanks for the update... For me it's working with REST_FRAMEWORK. What's the reason for changing it to SPECTACULAR_SETTINGS?Kinswoman
btw, the official DRF settings doesn't contain COMPONENT_SPLIT_REQUEST but in the drf-spectacular settingsContemporize
@Contemporize just tested on my project and it definitely works if I put COMPONENT_SPLIT_REQUESTS inside REST_FRAMEWORK. Didn't see anything in the docs, but my guess is that it falls back to use REST_FRAMEWORK settings if SPECTACULAR_SETTINGS isn't defined. However docs do state that it should be put in SPECTACULAR_SETTINGS so I think your edit did improve the answer. Thank you!Kinswoman
That's weird. I would like to do some digging into the problem. Could you share the minimal project or create another branch in this repo to have a closer look?Contemporize
SPECTACULAR_SETTINGS that are falsely put into REST_FRAMEWORK are not honored. drf-spectacular will not look there for its own settings.Germanism

© 2022 - 2024 — McMap. All rights reserved.