Django REST框架 + Django REST Swagger + ImageField

24

我创建了一个带有ImageField的简单模型,并且我想使用django-rest-framework + django-rest-swagger创建一个文档化的api视图,可以上传文件。

以下是我的代码:

models.py

from django.utils import timezone
from django.db import models

class MyModel(models.Model):

    source = models.ImageField(upload_to=u'/photos')
    is_active = models.BooleanField(default=False)
    created_at = models.DateTimeField(default=timezone.now)

    def __unicode__(self):
        return u"photo {0}".format(self.source.url)

serializer.py

from .models import MyModel

class MyModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = MyModel
        fields = [
            'id',
            'source',
            'created_at',
        ]

views.py

from rest_framework import generics
from .serializer import MyModelSerializer

class MyModelView(generics.CreateAPIView):
    serializer_class = MyModelSerializer
    parser_classes = (FileUploadParser, )

    def post(self, *args, **kwargs):
        """
            Create a MyModel
            ---
            parameters:
                - name: source
                  description: file
                  required: True
                  type: file
            responseMessages:
                - code: 201
                  message: Created
        """
        return super(MyModelView, self).post(self, *args, **kwargs)

urls.py

from weddings.api.views import MyModelView

urlpatterns = patterns(
    '',
    url(r'^/api/mymodel/$', MyModelView.as_view()),
)

对我来说,这应该很简单。但是,我无法使上传工作。我总是得到这个错误响应: 输入图像描述

我已经阅读了来自django-rest-framework的文档的这部分内容:

如果使用FileUploadParser的视图与文件名URL关键字参数一起调用,则该参数将用作文件名。如果未使用文件名URL关键字参数进行调用,则客户端必须在Content-Disposition HTTP标头中设置文件名。例如Content-Disposition:attachment; filename = upload.jpg。

然而,Header被传递给Request Payload属性(从Chrome控制台)的django-rest-swagger。

如果需要更多信息,请告诉我。

我正在使用Django==1.8.8djangorestframework==3.3.2django-rest-swagger==0.3.4


你是否安装了Pillow库?我认为Django Rest需要它来处理图像文件。 https://pypi.python.org/pypi/Pillow - Charles Haro
谢谢你,Charles..但是是的,我已经安装了Pillow。 - jarussi
3个回答

8
我通过对您的代码进行一些更改使其正常工作。
首先,在models.py中,将ImageField名称更改为file并使用相对路径上传文件夹。当您将文件作为二进制流上传时,它可以在request.data字典中的文件键下获得(request.data.get('file')),因此最干净的选择是将其映射到具有相同名称的模型字段。
from django.utils import timezone
from django.db import models


class MyModel(models.Model):

    file = models.ImageField(upload_to=u'photos')
    is_active = models.BooleanField(default=False)
    created_at = models.DateTimeField(default=timezone.now)

    def __unicode__(self):
        return u"photo {0}".format(self.file.url)

serializer.py中,将源字段改名为文件(file):
class MyModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = MyModel
        fields = ('id', 'file', 'created_at')

在 views.py 中,不要调用 super,而是调用 create():

from rest_framework import generics
from rest_framework.parsers import FileUploadParser

from .serializer import MyModelSerializer


class MyModelView(generics.CreateAPIView):
    serializer_class = MyModelSerializer
    parser_classes = (FileUploadParser,)

    def post(self, request, *args, **kwargs):
        """
            Create a MyModel
            ---
            parameters:
                - name: file
                  description: file
                  required: True
                  type: file
            responseMessages:
                - code: 201
                  message: Created
        """
        return self.create(request, *args, **kwargs)

我使用了Postman Chrome扩展来进行测试。我已将图像上传为二进制文件,并手动设置了两个头部:

Content-Disposition: attachment; filename=upload.jpg
Content-Type: */*

我尝试了您建议的更改,但现在我遇到了不同的错误: “上传有效图像。您上传的文件不是图像或已损坏。” - jarussi

6
这是我想出的最终解决方案:
from rest_framework import generics
from rest_framework.parsers import FormParser, MultiPartParser
from .serializer import MyModelSerializer

class MyModelView(generics.CreateAPIView):
    serializer_class = MyModelSerializer
    parser_classes = (FormParser, MultiPartParser)

    def post(self, *args, **kwargs):
        """
            Create a MyModel
            ---
            parameters:
                - name: source
                  description: file
                  required: True
                  type: file
            responseMessages:
                - code: 201
                  message: Created
        """
        return super(MyModelView, self).post(self, *args, **kwargs)

我只需要将解析器从 FileUploadParser 更改为 (FormParser, MultiPartParser) 即可。


你需要做什么才能让DRF或DRF-Swagger获取那个yml文件?我设置了一个像这样的文档字符串,但它只在“实现说明”下呈现为Markdown。 - Aaron McMillin
我的设置里面没有太多内容。基本上都是来自django-rest-swagger文档的基础设置。不过,我正在使用django-rest-swagger的0.3.7版本。 - jarussi

3

根据我的经验,FileUploadParser可以处理以下格式的请求:

    curl -X POST -H "Content-Type:multipart/form-data" \
                 -F "file=@{filename};type=image/jpg" \
                 https://endpoint.com/upload-uri/

您的视图中的request.data['file']将拥有该文件。

如果尝试使用Content-Type:multipart/form-data头,或许会更加顺利。


谢谢zemekeneng,但我仍然收到错误消息:{"file":["The submitted file is empty."]}。那没起作用。 - jarussi
也许尝试使用 curl 调用,并确保文件名在 {} 内,使其看起来像 file=@{path/to/file},如果这样可以工作,那么问题可能是客户端的。祝好运! - zemekeneng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接