drf-spectacular
                                
                                
                                
                                    drf-spectacular copied to clipboard
                            
                            
                            
                        Is there an option to add `encoding` field to the `requestBody`?
It will be easier to explain the problem on the example.
I have models with Many To Many relationship:
from django.db import models
class Tag(models.Model):
    name = models.CharField(max_length=255)
class Article(models.Model):
    title = models.CharField(max_length=100)
    tags = models.ManyToManyField(Tag, blank=True, related_name="articles")
And simple serializer with PrimaryKeyRelatedField (without read_only, so we can read and write m2m relation) :
from rest_framework import serializers
class ArticleSerializer(serializers.ModelSerializer):
    tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
    class Meta:
        model = Article
        fields = ['title', 'tags']
Then simple ViewSet:
from rest_framework import viewsets, permissions
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all().order_by("id")
    serializer_class = ArticleSerializer
    permission_classes = [permissions.AllowAny]
And Router:
from rest_framework import routers
from projects import views
router = routers.SimpleRouter()
router.register(r"articles", views.ArticleViewSet)
Let's assume that I have an Article with two tags:
curl -X 'GET' \
  'http://localhost:8000/api/articles/1/' \
  -H 'accept: application/json'
Response body:
{
  "title": "Article 1",
  "tags": [
    1,
    2
  ]
}
Now I would like to add third tag to the article, so I would use PATCH. I use Swagger UI with Content-Type set to application/json, and get something like this
curl -X 'PATCH' \
  'http://localhost:8000/api/articles/1/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'X-CSRFTOKEN: R0LLamjP2tLSjbFJzHtPK3PCRMfajzrIe75eM46h4arf8DxyIMlk4KyuHRpKrafn' \
  -d '{
  "tags": [
    1,2,3
  ]
}'
Response Body:
{
  "title": "Article 1",
  "tags": [
    1,
    2,
    3
  ]
}
Everything works fine so far.
There is a problem, when I would like to use application/x-www-form-urlencoded or multipart/form-data content type in Swagger UI:

The form looks ok, but generated Curl Request looks like this:
curl -X 'PATCH' \
  'http://localhost:8000/api/articles/1/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'X-CSRFTOKEN: R0LLamjP2tLSjbFJzHtPK3PCRMfajzrIe75eM46h4arf8DxyIMlk4KyuHRpKrafn' \
  -d 'tags=1,2'
Django Rest Framework doesn't accept parameters in that form tags=1,2, so the Response is:
{
  "statusCode": 400,
  "errorCode": "invalid",
  "details": {
    "tags": [
      "Incorrect type. Expected pk value, received str."
    ]
  }
}
Swagger UI should generate request like this:
curl -X 'PATCH' \
  'http://localhost:8000/api/articles/1/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'X-CSRFTOKEN: R0LLamjP2tLSjbFJzHtPK3PCRMfajzrIe75eM46h4arf8DxyIMlk4KyuHRpKrafn' \
  -d 'tags=1&tags=2'
Then it will be accepted by the API.
To achieve this (generating such format of request by Swagger UI), we need to add extra field encoding to OpenAPI schema: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object
Example of currently generated OpenAPI file:
openapi: 3.0.3
info:
  title: POSM Evaluator
  version: 1.0.0
  description: Manage POSM projects
paths:
  ...
  /api/articles/{id}/:
    patch:
      operationId: articlesPartialUpdate
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this article.
        required: true
      tags:
      - articles
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Article'
          description: ''
   ...
components:
  schemas:
    ...
    PatchedArticleRequest:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 100
        tags:
          type: array
          items:
            type: integer
    ...
Example of expected OpenAPI file:
openapi: 3.0.3
info:
  title: POSM Evaluator
  version: 1.0.0
  description: Manage POSM projects
paths:
  ...
  /api/articles/{id}/:
    patch:
      operationId: articlesPartialUpdate
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this article.
        required: true
      tags:
      - articles
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
            encoding:
              tags:
                style: form
                explode: true
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
            encoding:
              tags:
                style: form
                explode: true
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedArticleRequest'
      security:
      - cookieAuth: []
      - basicAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Article'
          description: ''
   ...
components:
  schemas:
    ...
    PatchedArticleRequest:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 100
        tags:
          type: array
          items:
            type: integer
    ...
The part:
            encoding:
              tags:
                style: form
                explode: true
Is the key.
Do you know if it is possible to add this part using drf-spectacular?
Hi @marcintabaka, so the short answer is no. It is currently not possible as we do not expose the encoding object and give only limited access to the media type object.
When I read the spec correctly, it can occur in both request and response. The problem is that we kind of compress down these objects to make OpenAPI more accessible. The added convenience limits the flexibility though. We do have something called OpenApiResponse, where encoding could potentially be inserted, but we have no such thing yet for the request.
Your use-case is absolutely valid, but at this point the question is whether this functionality warrants making the interface more complicated. I'm undecided because spectacular has been used a lot by a lot of people and no one ever asked about the encoding object.
@tfranzel thank you for your quick reply. What do you think about adding some global setting flag, to change Swagger UI behavior in this regard?
Simply if someone set this flag to True, then for every array type field, drf-spectacular will add this part to schema:
            encoding:
              {FIELD_NAME}:
                style: form
                explode: true
                                    
                                    
                                    
                                
I don't think a global setting is the right way. It is too coarse and also since it's bound to the field name, unwanted collisions might occur. It would be impossible to use this only selectively.
I'm also interested in getting access to the encoding property. My use is case is being able to specify particular image types
'contentType': ["image/png", "image/jpeg"]
 request={
            'multipart/form-data': {
                'type': 'object',
                'properties': {
                    
                    'images': {
                        'description': "List of images",
                        'type': 'array',
                        "items": {
                            "type": "string",
                            "format": "binary",
                        }
                    }
                },
            }