Django REST框架图像上传

41

我有一个产品模型:

def productFile(instance, filename):
    return '/'.join( ['products', str(instance.id), filename] )

class Product(models.Model):
    ...

    image = models.ImageField(
        upload_to=productFile,
        max_length=254, blank=True, null=True
    )
    ...

那么我有一个序列化程序:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = (
            ...
            'image',
            ...
        )

然后我有一些看法:

class ProductViewSet(BaseViewSet, viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

我该如何在Postman中上传图片?上传图片到模型的最佳实践是什么?谢谢。


2
去掉括号,serializer_class = ProductSerializer.. 如果你正在使用 ModelViewSet,那么你不需要再写什么了... ViewSet会为您处理几乎所有事情。 - zaidfazil
好的,谢谢。我该如何使用Postman或Ajax请求上传图片? - Danila Kulakov
当我在我的模型中使用它时,我会得到一个 ValueError("无法序列化函数: lambda")。 - Rony Azrak
@Rony Azrak,我刚刚更新了问题,并提供了一个可行的解决方案。请使用productFile方法。 - Danila Kulakov
你能把上传带有图片文件的模型数据的代码发给我吗?我正在寻找这方面的资料。我的代码显示了错误请求400 http://... - Muhammad Abdullah
4个回答

32

你可以创建一个专门用于上传图片的端点,类似于这样:

class ProductViewSet(BaseViewSet, viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    @detail_route(methods=['post'])
    def upload_docs(request):
        try:
            file = request.data['file']
        except KeyError:
            raise ParseError('Request has no resource file attached')
        product = Product.objects.create(image=file, ....)

你可以绕过那个解决方案

-- 更新: 这是从Postman上传的方法 enter image description here


3
您可以使用Postman上传文件,选择body中的form-data,选择文件类型为file并点击“选择文件”,在键字段上写入file。 请查看上面的更新内容。 - Shehab ElDin
1
谢谢!我可以有一个 URL,用方法“PUT”像 /api/products/33/upload_docs 吗? - Danila Kulakov
当然可以,但我没有检查是否可以使用“PUT”方法上传。 - Shehab ElDin
嗯,我刚试了一下你的解决方案,但是我收到了错误信息 {"detail":"Missing filename. Request should include a Content-Disposition header with a filename parameter."}。你知道怎么“关闭”这个要求吗?再次感谢! :) - Danila Kulakov
需要上传 filename,我认为这是 postman 的问题。如果您尝试使用任何前端代码测试此 API,则很容易设置文件名。 - Shehab ElDin
1
问题出在解析器上,我使用了FileUploadParser而不是MultiPartParser :) 谢谢! - Danila Kulakov

24
我最近开始学习Django,并且在上传图片方面遇到了一些问题。
以下是我所完成的所有步骤。
  1. 安装Pillow以使用ImageField

    pip install Pillow
    
  2. Settings.py中添加以下代码

    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    MEDIA_URL = '/media/' # 'http://myhost:port/media/'
    
  3. 在model.py中使用ImageField(创建nameFile函数用于创建文件夹和文件名)

    def upload_to(instance, filename):
        return '/'.join(['images', str(instance.name), filename])
    
    class UploadImageTest(models.Model):
        name = models.CharField(max_length=100)
        image = models.ImageField(upload_to=upload_to, blank=True, null=True)
    
  4. serializer.py

    class ImageSerializer(serializers.ModelSerializer):
        class Meta:
            model = UploadImageTest
            fields = ('name', 'image')
    
  5. views.py

    class ImageViewSet(ListAPIView):
        queryset = UploadImageTest.objects.all()
        serializer_class = ImageSerializer
    
        def post(self, request, *args, **kwargs):
            file = request.data['file']
            image = UploadImageTest.objects.create(image=file)
            return HttpResponse(json.dumps({'message': "Uploaded"}), status=200)
    
  6. urls.py:添加以下代码

    path('upload/', views.ImageViewSet.as_view(), name='upload'),
    
  7. admin.py:添加以下代码(用于在管理员界面中查看)

    admin.site.register(UploadImageTest)
    
  8. 在终端中执行以下命令

    python manage.py makemigrations
    python manage.py migrate
    

你尝试过在视图中使用 MultiPartParser 吗?另外,我建议你在 ImageViewSet 中创建一个新的端点来上传图片。 - Danila Kulakov
你能告诉我如何在请求中更新这个吗? - OurangZeb Khan

4

models.py

from django.db import models

class ImageUpload(models.Model):
    title = models.CharField(max_length=50)
    images = models.ImageField('images')

serializers.py

from rest_framework import serializers
from .models import ImageUpload

class ImageUploadSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = ImageUpload
        fields= (
            'title',
            'images'
        )

views.py

from rest_framework import viewsets
from .models import ImageUpload
from .serializers import ImageUploadSerializer

class ImageUploadViewSet(viewsets.ModelViewSet):
    queryset = ImageUpload.objects.all()
    serializer_class = ImageUploadSerializer

urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'imageupload', views.ImageUploadViewSet)

urlpatterns = [
    path('imageupload', include(router.urls)),
]

这是我认为最好的方式。谢谢。 - gulbaz khan

1

这对我有用

class UserAvatarUpload(ListAPIView):
    
    parser_classes = [MultiPartParser, FormParser]
    serializer_class = ImageSerializer

    def post(self, request, *args, **kwargs):
        username = request.data['username']
        file = request.data['image']
        user = MyModel.objects.get(username=username)
        user.image = file
        user.save()
        return Response("Image updated!", status=status.HTTP_200_OK)

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