You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向Django Rest Framework提交带图片表单时修复“必填字段”错误

问题与解决方案

问题描述

我创建了用于关联描述文件和图片的序列化器及对应的ModelViewSet视图,但通过HTML表单提交POST请求时,始终返回错误:

{
"image_file": [
"Required field."
]
}

原序列化器代码

class ImageFileSerializer(serializers.ModelSerializer):
    class Meta:
        model = ImageFile
        fields = ["image"]


class DescriptionFileSerializer(serializers.ModelSerializer):
    image_file = ImageFileSerializer(many=True)
    file_filename = serializers.CharField()
    user_id = serializers.ReadOnlyField(source="user.id")

    class Meta:
        model = DescriptionFile
        fields = ["pk", 'file_filename', 'user_id', 'title', 'description', 'line_video', 'tags',
                  'image_file', "time_create"]
        read_only_fields = ('time_create',)

原视图代码

class DescriptionFileView(ModelViewSet):
    queryset = DescriptionFile.objects.all().annotate(
        file_filename=F("file__filename")
    ).select_related("user", 'file').prefetch_related("tags", "image_file")
    serializer_class = DescriptionFileSerializer
    # permission_classes = [IsAuthorOrStaff]
    filter_backends = [SearchFilter]
    search_fields = ["time_create", "user__id", "tags__name"]
    parser_classes = (MultiPartParser, JSONParser)

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

提交用的HTML表单

<form action="http://localhost:8000/api/description/file/" method="post" enctype="multipart/form-data">
    <label for="title">Title:</label>
    <input type="text" id="title" name="title"><br><br>
    <label for="description">Description:</label>
    <textarea id="description" name="description"></textarea><br><br>
    <label for="line_video">Line Video:</label>
    <input type="text" id="line_video" name="line_video"><br><br>
    <label for="tags">Tags:</label>
    <input type="number" id="tags" name="tags"><br><br>
    <label for="file">File:</label>
    <input type="text" id="file" name="file_filename"><br><br>
    <label for="image_file">Image:</label>
    <input type="file" id="image_file" name="image_file.image"><br><br>
    <input type="submit" value="Submit">
</form>

环境版本

python==3.11.9
django==4.2.11
drf==3.15.1

错误原因

原序列化器中image_file = ImageFileSerializer(many=True)同时承担读写逻辑,但HTML表单提交的文件数据无法被嵌套的ModelSerializer直接解析,且该字段默认必填,导致验证失败。

修复方案

修改序列化器,拆分读写逻辑:将image_file设为只读,新增专门接收上传图片的write_only字段,并重写create方法手动处理关联逻辑:

class DescriptionFileSerializer(serializers.ModelSerializer):
    # 只读字段,用于返回已关联的图片数据
    image_file = ImageFileSerializer(many=True, read_only=True)
    # 只写字段,用于接收上传的图片列表
    uploaded_images = serializers.ListField(
        child=serializers.ImageField(allow_empty_file=False, use_url=False),
        write_only=True
    )

    class Meta:
        model = DescriptionFile
        fields = ["pk", 'file_filename', 'user_id', 'title', 'description', 'line_video', 'tags',
                  'image_file', "time_create", "uploaded_images"]
        read_only_fields = ('time_create',)

    def create(self, validated_data):
        # 提取上传的图片、标签、文件名数据
        images_data = validated_data.pop("uploaded_images", None)
        tags = validated_data.pop("tags", None)
        file_name = validated_data.pop("file_filename", None)
        
        # 关联对应的File对象
        file = File.objects.get(filename=file_name)
        des = DescriptionFile(file=file, **validated_data)
        des.save()
        
        # 设置多对多标签关联
        if tags:
            des.tags.set(tags)
        
        # 创建关联的ImageFile记录
        if images_data:
            for image in images_data:
                ImageFile.objects.create(description_file=des, image=image)
        
        return des

同时修改HTML表单的图片输入字段,调整名称并支持多文件上传:

<label for="image_file">Images:</label>
<input type="file" id="image_file" name="uploaded_images" multiple><br><br>

POST视图测试方法

1. Django测试客户端

编写测试用例模拟请求:

from django.test import TestCase, Client
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.auth.models import User
from .models import File, DescriptionFile, Tag

class DescriptionFileViewTest(TestCase):
    def setUp(self):
        self.client = Client()
        self.user = User.objects.create_user(username="testuser", password="testpass")
        self.tag = Tag.objects.create(name="testtag")
        self.file = File.objects.create(filename="testfile.txt")
        self.image = SimpleUploadedFile("test.jpg", b"file_content", content_type="image/jpeg")

    def test_create_description_file(self):
        self.client.login(username="testuser", password="testpass")
        data = {
            "title": "Test Title",
            "description": "Test Description",
            "line_video": "http://example.com/video",
            "tags": self.tag.pk,
            "file_filename": "testfile.txt",
            "uploaded_images": [self.image]
        }
        response = self.client.post("/api/description/file/", data, format="multipart")
        self.assertEqual(response.status_code, 201)
        self.assertTrue(DescriptionFile.objects.exists())
        self.assertTrue(DescriptionFile.objects.first().image_file.exists())

2. curl命令

终端直接发送请求:

curl -X POST -H "Authorization: Token YOUR_AUTH_TOKEN" -F "title=Test Title" -F "description=Test Description" -F "line_video=http://example.com/video" -F "tags=1" -F "file_filename=testfile.txt" -F "uploaded_images=@/path/to/your/image.jpg" http://localhost:8000/api/description/file/

3. API测试工具(如Postman)

  • 新建POST请求,URL设为http://localhost:8000/api/description/file/
  • Body选项卡选择form-data类型
  • 添加文本字段(title、description等),uploaded_images选择File类型并上传图片
  • 若需认证,在Headers中添加认证信息(如Token)
  • 发送请求并查看响应

内容的提问来源于stack exchange,提问作者dratyti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:23:21