Depois de passar 1 dia nisso, descobri que ...
Para alguém que precisa fazer upload de um arquivo e enviar alguns dados, não há uma maneira direta de fazer isso funcionar. Há um problema aberto nas especificações da API JS para isso. Uma possibilidade que vi é usar multipart/related
como mostrado aqui , mas acho muito difícil implementá-lo no DRF.
Por fim, o que implementei foi enviar a solicitação como formdata
. Você enviaria cada arquivo como arquivo e todos os outros dados como texto. Agora, para enviar os dados como texto, você tem duas opções. caso 1) você pode enviar cada dado como par de valores-chave ou caso 2) você pode ter uma única chave chamada dados e enviar o json inteiro como string em valor.
O primeiro método funcionaria imediatamente se você tiver campos simples, mas será um problema se você tiver serializações aninhadas. O analisador multipartes não será capaz de analisar os campos aninhados.
Abaixo, estou fornecendo a implementação para ambos os casos
Models.py
class Posts(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
caption = models.TextField(max_length=1000)
media = models.ImageField(blank=True, default="", upload_to="posts/")
tags = models.ManyToManyField('Tags', related_name='posts')
serializers.py -> nenhuma mudança especial necessária, não mostrando meu serializador aqui como muito extenso devido à implementação gravável do campo ManyToMany.
views.py
class PostsViewset(viewsets.ModelViewSet):
serializer_class = PostsSerializer
#parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
#parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
queryset = Posts.objects.all()
lookup_field = 'id'
Agora, se você estiver seguindo o primeiro método e apenas enviando dados não Json como pares de valores-chave, não precisará de uma classe de analisador customizada. O MultipartParser do DRF fará o trabalho. Mas para o segundo caso, ou se você tiver serializadores aninhados (como eu mostrei), você precisará do analisador personalizado conforme mostrado abaixo.
utils.py
from django.http import QueryDict
import json
from rest_framework import parsers
class MultipartJsonParser(parsers.MultiPartParser):
def parse(self, stream, media_type=None, parser_context=None):
result = super().parse(
stream,
media_type=media_type,
parser_context=parser_context
)
data = {}
# for case1 with nested serializers
# parse each field with json
for key, value in result.data.items():
if type(value) != str:
data[key] = value
continue
if '{' in value or "[" in value:
try:
data[key] = json.loads(value)
except ValueError:
data[key] = value
else:
data[key] = value
# for case 2
# find the data field and parse it
data = json.loads(result.data["data"])
qdict = QueryDict('', mutable=True)
qdict.update(data)
return parsers.DataAndFiles(qdict, result.files)
Este serializador basicamente analisaria qualquer conteúdo json nos valores.
O exemplo de solicitação no correio para ambos os casos: caso 1 ,
Caso 2