Después de pasar 1 día en esto, descubrí que ...
Para alguien que necesita cargar un archivo y enviar algunos datos, no existe una forma directa de hacer que funcione. Hay un problema abierto en las especificaciones de la api json para esto. Una posibilidad que he visto es usarlo multipart/related
como se muestra aquí , pero creo que es muy difícil implementarlo en drf.
Finalmente lo que había implementado era enviar la solicitud como formdata
. Enviaría cada archivo como archivo y todos los demás datos como texto. Ahora, para enviar los datos como texto, tiene dos opciones. caso 1) puede enviar cada dato como par clave-valor o caso 2) puede tener una sola clave llamada datos y enviar todo el json como cadena en valor.
El primer método funcionaría de inmediato si tiene campos simples, pero será un problema si tiene serializaciones anidadas. El analizador multiparte no podrá analizar los campos anidados.
A continuación, proporciono la implementación para ambos 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 -> no se necesitan cambios especiales, no muestra mi serializador aquí porque es demasiado largo debido a la implementación de campo ManyToMany que se puede escribir.
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'
Ahora, si está siguiendo el primer método y solo envía datos que no son de Json como pares clave-valor, no necesita una clase de analizador personalizada. DRF'd MultipartParser hará el trabajo. Pero para el segundo caso o si tiene serializadores anidados (como he mostrado), necesitará un analizador personalizado como se muestra a continuación.
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 básicamente analizaría cualquier contenido json en los valores.
El ejemplo de solicitud en post man para ambos casos: caso 1 ,
Caso 2