Django REST Framework文件上传报错:已传文件仍提示文件字段必填
问题原因
你的代码存在两个核心问题导致表单验证失败:
- 文件字段传递方式错误:Django的ModelForm处理上传文件时,文件字段必须通过
files参数单独传递,不能和普通字段一起放在data参数里。你当前把file_store放到data中,表单无法识别这是上传文件,因此判定file_store字段为空,触发必填校验错误。 - upload_to路径生成逻辑错误:模型中
file_store的upload_to使用f-string结合datetime.now(),会在模型类加载时就固定时间值,所有后续上传的文件都会存入同一个静态目录,无法实现动态生成上传路径的需求。
修复方案
1. 修正视图中表单初始化方式
将文件字段与普通字段拆分传递,文件字段通过files参数传入:
class FileViews(APIView): authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] def post(self,request): try: file = request.FILES.get("file") user_id = request.POST.get("user_id") # 改用get避免KeyError if not file or not user_id: return Response({"message": "Both 'file' and 'user_id' are required."}, status=rest_framework.status.HTTP_400_BAD_REQUEST) # 拆分data和files参数,分别传递普通字段与文件字段 file_form = FilesForm( data={"user_id": user_id}, files={"file_store": file} ) if file_form.is_valid(): file_form.save() print(file_form.cleaned_data) response = {"message" : "File is uploaded"} return Response(response , status = rest_framework.status.HTTP_201_CREATED) else: response = {"message" : file_form.errors} return Response(response , status=rest_framework.status.HTTP_400_BAD_REQUEST) except Exception as e: print(e) response = {"message" : str(e)} return Response(response , status = rest_framework.status.HTTP_500_INTERNAL_SERVER_ERROR)
2. 修复模型的动态上传路径
改用可调用函数生成动态路径,确保每次上传都会生成新的时间目录:
import datetime from django.db import models def get_upload_path(instance, filename): # 按时间生成唯一目录,格式示例:20240520_143020/filename time_str = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") return f"../uploads/{time_str}/{filename}" class Files(models.Model): user_id = models.IntegerField(unique=True) file_size = models.PositiveIntegerField("file size(in Bytes)", default=0) file_store = models.FileField(upload_to=get_upload_path) file_compressed = models.BooleanField(default=False)
3. 优化:自动填充文件大小(可选)
在表单中添加clean方法,自动从上传文件中获取文件大小,避免手动传递出错:
from django import forms from .models import Files class FilesForm(forms.ModelForm): class Meta: model = Files fields = '__all__' def clean(self): cleaned_data = super().clean() file = cleaned_data.get("file_store") if file: # 自动从文件对象中提取大小 cleaned_data["file_size"] = file.size return cleaned_data
内容的提问来源于stack exchange,提问作者Chahat Sagar
相关产品推荐
相关产品推荐

