Django表单is_valid()始终返回True,FileField验证失效求助
文件上传表单验证失效问题
提交表单后,无论上传文件是否符合要求,form.is_valid()始终返回True,页面直接跳转到首页并显示成功提示,无法理解UpdateChecklistForm中的clean()方法为何不生效。
相关代码
forms.py
class UpdateChecklistForm(forms.ModelForm): class Meta: model = CheckList exclude = ['author'] isps_upload = forms.FileField( label='ISPS report', widget=forms.FileInput(attrs={'accept': 'pdf'}), required=False ) crewlist_upload = forms.FileField( label='Crew List', widget=forms.FileInput(attrs={'accept': 'pdf'}), required=False ) def clean(self): self.check_isps() self.check_crewlist() return self.cleaned_data def check_isps(self): try: content = self.cleaned_data["isps_upload"] content_type = content.content_type.split('/')[0] if content.size > int(MAX_UPLOAD_SIZE): self.add_error( 'isps_upload', _(f"Please keep file size under {(filesizeformat(MAX_UPLOAD_SIZE))}. " f"Current file size {filesizeformat(content.size)}") ) extension = os.path.splitext(content.name)[1] VALID_EXTENSIONS = ['.pdf', '.doc', '.docx'] if not extension.lower() in VALID_EXTENSIONS: self.add_error( 'isps_upload', _('Only files with "pdf" or "doc/docx" extensions are supported, ' 'received: "%s" file.' % extension) ) return content except AttributeError: pass def check_crewlist(self): try: content = self.cleaned_data["crewlist_upload"] content_type = content.content_type.split('/')[0] if content.size > int(MAX_UPLOAD_SIZE): self.add_error( 'crewlist_upload', _(f"Please keep file size under {(filesizeformat(MAX_UPLOAD_SIZE))}. " f"Current file size {filesizeformat(content.size)}") ) extension = os.path.splitext(content.name)[1] # [0] returns path+filename VALID_EXTENSIONS = ['.pdf', '.doc', '.docx'] if not extension.lower() in VALID_EXTENSIONS: self.add_error( 'crewlist_upload', _('Only files with "pdf" or "doc/docx" extensions are supported, ' 'received: "%s" file.' % extension) ) return content except AttributeError: pass
views.py
@login_required def update_checklist(request, pk): instance = get_object_or_404(CheckList, id=pk) form = UpdateChecklistForm(request.POST or None, instance=instance) if request.method == 'POST': if form.is_valid(): instance = form.save(commit=False) instance.author = request.user ETA = request.POST['ETA'] try: dispatcher = request.POST['dispatcher'] if dispatcher: dispatcher = 'Done' except Exception as e: dispatcher = '-//-' instance.author = request.user try: user = request.user if hasattr(user, '_wrapped') and hasattr(user, '_setup'): if user._wrapped.__class__ == object: user._setup() user = user._wrapped if user: user = user.last_name + " " + user.first_name except Exception as e: user = '-//-' instance.save() sendTelegram(...some stuff...) messages.success(request, 'Success') return redirect('home') else: return HttpResponse('error') context = { 'form': form } return render(request, 'someurl/update.html', context)
问题原因及解决方案
1. 核心问题:未传递request.FILES到表单
Django处理文件上传时,必须将request.FILES传入表单构造函数,否则表单无法获取上传的文件,cleaned_data中对应的文件字段为None,导致check_isps和check_crewlist里的验证逻辑因捕获AttributeError直接跳过,不会添加错误信息,form.is_valid()自然返回True。
修改视图中表单初始化代码:
form = UpdateChecklistForm(request.POST or None, request.FILES or None, instance=instance)
2. 优化表单验证逻辑
替换宽泛的
AttributeError捕获,改为明确判断字段是否存在,避免忽略其他潜在错误:def check_isps(self): content = self.cleaned_data.get("isps_upload") if not content: return if content.size > int(MAX_UPLOAD_SIZE): self.add_error( 'isps_upload', _(f"Please keep file size under {(filesizeformat(MAX_UPLOAD_SIZE))}. " f"Current file size {filesizeformat(content.size)}") ) extension = os.path.splitext(content.name)[1].lower() VALID_EXTENSIONS = ['.pdf', '.doc', '.docx'] if extension not in VALID_EXTENSIONS: self.add_error( 'isps_upload', _('Only files with "pdf" or "doc/docx" extensions are supported, ' 'received: "%s" file.' % extension) )check_crewlist方法做同样修改。删除未使用的
content_type变量,简化代码。
3. 视图中通过表单获取数据
视图中直接从request.POST读取数据的做法不规范,应该通过表单的cleaned_data获取,确保数据经过表单验证:
if form.is_valid(): instance = form.save(commit=False) instance.author = request.user ETA = form.cleaned_data['ETA'] dispatcher = form.cleaned_data.get('dispatcher', '-//-') if dispatcher: dispatcher = 'Done' # 后续逻辑保留
内容的提问来源于stack exchange,提问作者Ches_Ter
相关产品推荐
相关产品推荐

