En utilisant Django Rest Framework, Comment puis-je télécharger un fichier et envoyer une charge JSON?
j'essaie d'écrire un gestionnaire D'API Rest Framework Django qui peut recevoir un fichier ainsi qu'une charge JSON. J'ai configuré le MultiPartParser en tant que gestionnaire.
cependant, il semble que je ne puisse pas faire les deux. Si j'envoie la charge utile avec le fichier en tant que requête multi-pièces, la charge utile JSON est disponible de manière fragmentée dans la requête.données (première partie du texte jusqu'au premier point-virgule comme clé, le reste étant les données). Je peux envoyer les paramètres dans les paramètres de forme standard juste bien - mais le reste de mon API accepte les charges JSON et je voulais être cohérent. Demande.le corps ne peut pas être lu car il soulève *** RawPostDataException: You cannot access body after reading from request's data stream
Par exemple, un fichier et cette charge dans le corps de la requête:{"title":"Document Title", "description":"Doc Description"}
Devient:<QueryDict: {u'fileUpload': [<InMemoryUploadedFile: 20150504_115355.jpg (image/jpeg)>, <InMemoryUploadedFile: Front end lead.doc (application/msword)>], u'{%22title%22': [u'"Document Title", "description":"Doc Description"}']}>
Est-il possible de faire cela? Puis-je manger mon gâteau, le garder et ne pas prendre du poids?
Modifier: Il a été suggéré que cela pourrait être une copie de Django REST Framework télécharger l'image: "The les données soumises n'étaient pas un fichier". Il n'est pas. Le téléchargement et la demande se fait en plusieurs parties, et de garder à l'esprit le fichier et le télécharger c'est bien. Je peux même remplir la demande avec des variables de formulaire standard. Mais je veux voir si je peux avoir un JSON à la place.
4 réponses
je sais que c'est un vieux fil, mais je viens de tomber sur ceci. J'ai eu à utiliser MultiPartParser
afin d'obtenir mon fichier et de données supplémentaires pour trouver ensemble. Voici ce que mon code ressemble à ceci:
# views.py
class FileUploadView(views.APIView):
parser_classes = (MultiPartParser,)
def put(self, request, filename, format=None):
file_obj = request.data['file']
ftype = request.data['ftype']
caption = request.data['caption']
# ...
# do some stuff with uploaded file
# ...
return Response(status=204)
mon code AngularJS en utilisant ng-file-upload
est:
file.upload = Upload.upload({
url: "/api/picture/upload/" + file.name,
data: {
file: file,
ftype: 'final',
caption: 'This is an image caption'
}
});
j'envoie JSON et une image pour créer/mettre à jour un objet produit. Ci-dessous, un apiview create qui fonctionne pour moi.
Sérialiseur
class ProductCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
"id",
"product_name",
"product_description",
"product_price",
]
def create(self,validated_data):
return Product.objects.create(**validated_data)
View
from rest_framework import generics,status
from rest_framework.parsers import FormParser,MultiPartParser
class ProductCreateAPIView(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductCreateSerializer
permission_classes = [IsAdminOrIsSelf,]
parser_classes = (MultiPartParser,FormParser,)
def perform_create(self,serializer,format=None):
owner = self.request.user
if self.request.data.get('image') is not None:
product_image = self.request.data.get('image')
serializer.save(owner=owner,product_image=product_image)
else:
serializer.save(owner=owner)
Exemple de test:
def test_product_creation_with_image(self):
url = reverse('products_create_api')
self.client.login(username='testaccount',password='testaccount')
data = {
"product_name" : "Potatoes",
"product_description" : "Amazing Potatoes",
"image" : open("local-filename.jpg","rb")
}
response = self.client.post(url,data)
self.assertEqual(response.status_code,status.HTTP_201_CREATED)
pour quelqu'un qui a besoin de télécharger un fichier et d'envoyer des données, il n'y a pas de moyen simple fwd que vous pouvez obtenir à travailler. Il y a un question en json api spécifications pour. Une possibilité que j'ai vu, c'est d'utiliser multipart/related
comme le montre ici, mais je pense que c'est très difficile de l'implémenter dans drf.
finalement ce que j'avais implémenté était d'envoyer la demande comme formdata
. Vous enverriez chaque fichier comme le fichier et toutes les autres données sous forme de texte.
Maintenant pour l'envoi les données sous forme de texte vous pouvez avoir une seule clé appelée et envoyer le JSON entier comme chaîne de valeur en valeur.
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 -> pas de modifications spéciales nécessaires, ne pas montrer mon serializer ici comme son trop long en raison de la ManyToMany writable implementation de champ.
views.py
class PostsViewset(viewsets.ModelViewSet):
serializer_class = PostsSerializer
parser_classes = (MultipartJsonParser, parsers.JSONParser)
queryset = Posts.objects.all()
lookup_field = 'id'
Vous aurez besoin de parseur personnalisé comme indiqué ci-dessous pour l'analyse json.
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 = {}
# 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)
j'ai un problème similaire, voici ma solution:
ajoutez D'abord ceci à votre configuration (settings.py):
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.MultiPartParser',
'rest_framework.parsers.FileUploadParser',
),
puis dans votre Serializer (ex: 'file'):
file = serializers.FileField()
Et à votre avis, ajouter:
parser_classes = (FileUploadParser, JSONParser)
avec ceci je pourrais poster à la fois un fichier et divers champs, mais vous devez spécifier:
- le post format comme "multipart'
- et cet en-tête http:
HTTP_CONTENT_DISPOSITION= " attachment; filename = your_file_name.jpg"