Django Rest框架文件上传

 让安丷全筑起心灵的屏障 发布于 2023-02-11 23:33

我正在使用Django Rest Framework和AngularJs上传文件.我的视图文件如下所示:

class ProductList(APIView):
    authentication_classes = (authentication.TokenAuthentication,)
    def get(self,request):
        if request.user.is_authenticated(): 
            userCompanyId = request.user.get_profile().companyId
            products = Product.objects.filter(company = userCompanyId)
            serializer = ProductSerializer(products,many=True)
            return Response(serializer.data)

    def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(data=request.DATA)

由于post方法的最后一行应该返回所有数据,我有几个问题:

如何检查是否有任何内容request.FILES

如何序列化文件字段?

我该如何使用解析器?

小智.. 72

我正在使用相同的堆栈,并且还在寻找文件上传的示例,但我的情况更简单,因为我使用ModelViewSet而不是APIView.密钥竟然是pre_save钩子.我最终将它与angular-file-upload模块一起使用,如下所示:

# Django
class ExperimentViewSet(ModelViewSet):
    queryset = Experiment.objects.all()
    serializer_class = ExperimentSerializer

    def pre_save(self, obj):
        obj.samplesheet = self.request.FILES.get('file')

class Experiment(Model):
    notes = TextField(blank=True)
    samplesheet = FileField(blank=True, default='')
    user = ForeignKey(User, related_name='experiments')

class ExperimentSerializer(ModelSerializer):
    class Meta:
        model = Experiment
        fields = ('id', 'notes', 'samplesheet', 'user')

// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
    $scope.submit = function(files, exp) {
        $upload.upload({
            url: '/api/experiments/' + exp.id + '/',
            method: 'PUT',
            data: {user: exp.user.id},
            file: files[0]
        });
    };
});

在drf 3.x中不推荐使用pre_save (10认同)


pleasedontbe.. 52

使用FileUploadParser,它都在请求中.使用put方法代替,你会在docs中找到一个例子:)

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

    def put(self, request, filename, format=None):
        file_obj = request.FILES['file']
        # do some stuff with uploaded file
        return Response(status=204)

@pleasedontbelong为什么在这里使用PUT方法而不是POST? (6认同)

嗨@pleasedontbelong,如果它正在创建一个新记录,它会改为POST吗?它还能用于FileUploadParser吗? (5认同)

对于第二个@ x-yuri,DRF抱怨当我使用FileUploadParser时Content-Disposition标头为空。MultiPartParser更为简单,因为它仅假定文件名是“表单”字段中的给定文件名。 (2认同)


Vipul J.. 29

最后,我可以使用Django上传图像.这是我的工作代码

views.py

class FileUploadView(APIView):
    parser_classes = (FileUploadParser, )

    def post(self, request, format='jpg'):
        up_file = request.FILES['file']
        destination = open('/Users/Username/' + up_file.name, 'wb+')
        for chunk in up_file.chunks():
            destination.write(chunk)
            destination.close()

        # ...
        # do some stuff with uploaded file
        # ...
        return Response(up_file.name, status.HTTP_201_CREATED)

urls.py

urlpatterns = patterns('', 
url(r'^imageUpload', views.FileUploadView.as_view())

curl请求上传

curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload

为什么destination.close()放在for循环的内部? (14认同)

似乎最好使用`with open('/ Users/Username /'+ up_file.name,'wb +')作为目标:`并完全删除关闭 (9认同)


Nithin.. 8

在此上花了1天后,我发现...

对于需要上载文件并发送一些数据的人,没有直接的前进方式可以使它工作。json api规范中有一个未解决的问题。我看到的一种可能性是使用此处multipart/related所示的方法,但是我认为很难在drf中实现它。

最后,我实现的是将请求发送为formdata。您将每个文件作为文件发送,所有其他数据作为文本发送。现在,以文本形式发送数据有两种选择。情况1)您可以将每个数据作为键值对发送,或者情况2)您可以有一个名为data的键,并将整个json作为值字符串发送。

如果您具有简单的字段,则第一种方法开箱即用,但如果嵌套了序列化,则将是一个问题。多部分解析器将无法解析嵌套字段。

下面我提供两种情况的实现

型号

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->无需特殊更改,由于可写的ManyToMany Field含义,此处没有显示我的序列化程序太长。

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'

现在,如果您遵循第一种方法,并且仅将非Json数据作为键值对发送,则不需要自定义解析器类。DRF的MultipartParser将完成这项工作。但是对于第二种情况,或者如果您有嵌套的序列化器(如我所示),则需要自定义解析器,如下所示。

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)

该序列化程序将基本上解析值中的所有json内容。

邮递员在两种情况下的请求示例:案例1

情况二

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有