向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
相关产品推荐
相关产品推荐

