Dopo aver trascorso 1 giorno su questo, ho capito che ...
Per qualcuno che ha bisogno di caricare un file e inviare alcuni dati, non esiste un modo diretto per farlo funzionare. C'è un problema aperto nelle specifiche API JSON per questo. Una possibilità che ho visto è quella di utilizzare multipart/related
come mostrato qui , ma penso che sia molto difficile implementarlo in drf.
Infine quello che avevo implementato era inviare la richiesta come formdata
. Dovresti inviare ogni file come file e tutti gli altri dati come testo. Ora per inviare i dati come testo hai due scelte. caso 1) puoi inviare ogni dato come coppia di valori chiave o caso 2) puoi avere una singola chiave chiamata dati e inviare l'intero json come stringa in valore.
Il primo metodo funzionerebbe immediatamente se si dispone di campi semplici, ma sarà un problema se si dispone di serializzazioni nidificate. Il parser multiparte non sarà in grado di analizzare i campi nidificati.
Di seguito fornisco l'implementazione per entrambi i casi
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 -> non sono necessarie modifiche speciali, non mostrare il mio serializzatore qui perché è troppo lungo a causa dell'implementazione del 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'
Ora, se stai seguendo il primo metodo e invii solo dati non Json come coppie di valori chiave, non hai bisogno di una classe parser personalizzata. DRF'd MultipartParser farà il lavoro. Ma per il secondo caso o se hai serializzatori annidati (come ho mostrato) avrai bisogno di un parser personalizzato come mostrato di seguito.
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)
Questo serializzatore analizzerebbe fondamentalmente qualsiasi contenuto JSON nei valori.
L'esempio di richiesta in post man per entrambi i casi: caso 1 ,
Caso 2