如何在django rest框架中上传多个文件

时间:2016-09-22 17:32:49

标签: python angularjs django django-rest-framework

在django rest框架中,我可以使用danialfarid/ng-file-upload

上传单个文件

views.py:

class PhotoViewSet(viewsets.ModelViewSet):
    serializer_class = PhotoSerializer
    parser_classes = (MultiPartParser, FormParser,)
    queryset=Photo.objects.all()

    def perform_create(self, serializer):
        serializer.save(blogs=Blogs.objects.latest('created_at'),
                   image=self.request.data.get('image'))

serializers.py:

class PhotoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Photo

models.py:

class Photo(models.Model):
    blogs = models.ForeignKey(Blogs, related_name='blogs_img')
    image = models.ImageField(upload_to=content_file_name)

当我尝试上传多个文件时。我进去了

chrome开发者工具: 请求有效负载

------WebKitFormBoundaryjOsYUxPLKB1N69Zn
Content-Disposition: form-data; name="image[0]"; filename="datacable.jpg"
Content-Type: image/jpeg


------WebKitFormBoundaryjOsYUxPLKB1N69Zn
Content-Disposition: form-data; name="image[1]"; filename="datacable2.jpg"
Content-Type: image/jpeg

响应:

{"image":["No file was submitted."]}

我不知道如何编写用于上传多个文件的序列化程序。任何人都请帮忙。

先谢谢

5 个答案:

答案 0 :(得分:15)

我设法解决这个问题,我希望它能帮助社区

serializers.py:

class FileListSerializer ( serializers.Serializer ) :
    image = serializers.ListField(
                       child=serializers.FileField( max_length=100000,
                                         allow_empty_file=False,
                                         use_url=False )
                                )
    def create(self, validated_data):
        blogs=Blogs.objects.latest('created_at')
        image=validated_data.pop('image')
        for img in image:
            photo=Photo.objects.create(image=img,blogs=blogs,**validated_data)
        return photo

class PhotoSerializer(serializers.ModelSerializer):

    class Meta:
        model = Photo
        read_only_fields = ("blogs",)

views.py:

class PhotoViewSet(viewsets.ModelViewSet):
    serializer_class = FileListSerializer
    parser_classes = (MultiPartParser, FormParser,)
    queryset=Photo.objects.all()

答案 1 :(得分:1)

我不太了解它,但这是有效的...... 这是我的观点。

def perform_create(self, serializer):
    obj = serializer.save()
    for f in self.request.data.getlist('files'):
        mf = MyFile.objects.create(file=f)
        obj.files.add(mf)

答案 2 :(得分:0)

花了我一段时间才找到有效的解决方案, 我想与您分享最终对我有用的东西。 我正在使用reactjsDRF

这是我的模特:

class MediaFile(models.Model):
    article = models.ForeignKey(Articles, on_delete=models.CASCADE, null=True)
    caption = models.CharField(max_length=500, null=True, blank=True)
    file = models.FileField('photo of article', upload_to=set_filename,
                            blank=True, null=True, default='')
    added = models.DateTimeField(auto_now_add=True)

视图是标准的viewsets.ModelViewSet

class MediaFilesViewSet(viewsets.ModelViewSet):
    serializer_class = FileListSerializer
    parser_classes = (parsers.MultiPartParser, parsers.FormParser,)
    queryset=MediaFile.objects.all()

ArticleSerializer中,我添加了:

def create(self, validated_data):
    request = self.context.get('request')
    user = request.user
    instance = Articles.objects.create(**validated_data)
    instance.publisher = user
    instance.save()
    images = request.FILES
    if images:
        try:
            for f in images.getlist('mediafiles'):
                instance.mediafile_set.get_or_create(file=f, caption=f.name)
                instance.save()
        except Exception as e:
            print(e)
    return instance

FRONTEND结构:

postChangeImage = (event) =>{
    this.setState({
        is_images: true
    });
    let files = event.target.files;
    const files_array = Object.values(files);

    this.setState(
        {imagefile: files}
    );

    console.log('IMAGES ARRAY', files_array);
    files_array.map(value => {
        const urls = URL.createObjectURL(value);
        this.setState((prevState)=>(
            {postfolio:[urls, ...prevState.postfolio]}
            ));
    });

};

和发布:

for (let i=0; i< this.state.imagefile.length; i++) {
                    form_data.append(`mediafiles`, this.state.imagefile[i]);
                }

答案 3 :(得分:0)

以下是在博客 api 上上传多个文件的方法:

models.py

class Blogs(models.Model):
    ...
 

class Photo(models.Model):
    blogs = models.ForeignKey(Blogs, related_name='blogs_img')
    image = models.ImageField(upload_to=content_file_name)

serializers.py

class PhotoSerializer(serializers.ModelSerializer):

    class Meta:
        model = Photo
        fields = ['blogs', 'image',]


class BlogsSerializer(serializers.ModelSerializer):
    photos = serializers.SerializerMethodField()

    def get_photos(self, obj):
        photos = Photo.objects.filter(blogs=obj)
        return PhotoSerializer(photos, many=True, read_only=False).data

    class Meta:
        model = Blogs
        fields = [
            ...
            'photos',
    ]

views.py

class BlogsViewSet(viewsets.ModelViewSet):
    serializer_class = BlogsSerializer
    queryset = Blogs.objects.all()

    def create(self, request, *args, **kwargs):
        instance_data = request.data
        data = {key: value for key, value in instance_data.items()}
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        instance = serializer.save()

        if request.FILES:
            photos = dict((request.FILES).lists()).get('photos', None)
            if photos:
                for photo in photos:
                    photo_data = {}
                    photo_data["blogs"] = instance.pk
                    photo_data["image"] = photo
                    photo_serializer = PhotoSerializer(data=photo_data)
                    photo_serializer.is_valid(raise_exception=True)
                    photo_serializer.save()

        return Response(serializer.data)

答案 4 :(得分:0)

这个问题的最佳答案对我不起作用,但查尔斯的建议非常有效。就我而言,我需要上传多个文件并将它们分配给特定批次。每个批次都分配给一个特定的用户。

以下是使用 ReactJS 发出 POST 请求的更多上下文,以及使用的序列化程序和 Postman 窗口:

api.py

from convert_files.models import File
from rest_framework import viewsets, permissions
from rest_framework.parsers import MultiPartParser, JSONParser
from .serializers import BatchSerializer

class BatchViewSet(viewsets.ModelViewSet):
    permission_classes = [
        permissions.IsAuthenticated
    ]

    def perform_create(self, serializer):
        obj = serializer.save(owner=self.request.user)
        for f in self.request.data.getlist('files'):
            mf = File.objects.create(office_file=f)
            obj.files.add(mf)

    parser_classes = (MultiPartParser, JSONParser, )

    serializer_class = BatchSerializer

    http_method_names = ['get','post','delete','put','patch', 'head']

    def get_queryset(self):
        return self.request.user.batches.all()

serializers.py

from rest_framework import serializers
from convert_files.models import File, Batch

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = '__all__'


class BatchSerializer(serializers.ModelSerializer):
    files = FileSerializer(many=True, required = False)

    class Meta:
        model = Batch
        fields =  '__all__'

models.py

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User

from .extra import ContentTypeRestrictedFileField

def make_upload_path(instance, filename):
    """Generates upload path for FileField"""
    return settings.OFFICE_OUTPUT_FILES_URL + "/%s" % (filename)

class Batch(models.Model):
    name = models.CharField(max_length=100, blank=True)
    description = models.TextField(blank=True)
    date_posted = models.DateTimeField(default=datetime.datetime.now)
    owner = models.ForeignKey(User, related_name="batches", 
                            on_delete=models.CASCADE, null=True)

class File(models.Model):
    name = models.CharField(max_length=100, blank=True)
    office_file = ContentTypeRestrictedFileField(
        upload_to           = make_upload_path,
        content_types       = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                            'application/vnd.ms-excel','application/msword',
                            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
        max_upload_size     = 10485760,
    )

    files = models.ForeignKey(Batch, on_delete=models.CASCADE, null=True, 
                                    related_name='files', related_query_name='files')

FileUpload.js

import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { addBatch } from '../actions/batches';

function FileUpload() {

  const dispatch = useDispatch();
  let formData = new FormData()

  const onDrop = useCallback((acceptedFiles) => {
    for (var i = 0; i < acceptedFiles.length; i++) {
      formData.append("files", acceptedFiles[i], acceptedFiles[i].name)
    }
    dispatch(addBatch(formData));
  })

...

邮递员

Image of POST request in Postman for Multiple File Upload to DRF