我有一个带有所需FileField的Django表单(不是ModelForm)。根据{{3}},FileField的验证应验证非空文件数据已绑定到表单,但我没有看到此行为。相反,我能够提交没有文件的表单,表单通过验证。预期的行为是验证失败。
当在表单中指定文件时,事情按预期工作。
我的表单看起来像这样:
class BucketUploadForm(forms.Form):
file = forms.FileField(required=True) # required=True is the default, but I'm being explicit
def clean(self):
upload_to = '/some/path'
upload_to += self.cleaned_data['file'].name # this is raising a KeyError
我的观点看起来像这样:
def bucket_upload(request):
if request.method == 'POST':
form = BucketUploadForm(request.POST, request.FILES)
if form.is_valid(): # this is raising the aforementioned KeyError when no file is submitted
do_stuff()
return HttpResponseRedirect(some_url)
else:
form = BucketUploadForm(initial=request.GET)
return render_to_response('handin/bucket_upload.html', {'form': form}, context_instance=RequestContext(request))
我的模板看起来像这样:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Upload" />
</form>
回溯看起来像这样:
Django Version: 1.3.1
Python Version: 2.7.3
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'django.contrib.admindocs',
'hgrepo',
'sshkey',
'handin',
'accounts']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware')
Traceback:
File "/usr/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
111. response = callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python2.7/dist-packages/django/contrib/auth/decorators.py" in _wrapped_view
23. return view_func(request, *args, **kwargs)
File "/usr/lib/python2.7/dist-packages/django/views/decorators/http.py" in inner
45. return func(request, *args, **kwargs)
File "/home/sduckwo/projects/webhandin/webhandin/handin/views.py" in bucket_upload
461. if form.is_valid():
File "/usr/lib/python2.7/dist-packages/django/forms/forms.py" in is_valid
121. return self.is_bound and not bool(self.errors)
File "/usr/lib/python2.7/dist-packages/django/forms/forms.py" in _get_errors
112. self.full_clean()
File "/usr/lib/python2.7/dist-packages/django/forms/forms.py" in full_clean
268. self._clean_form()
File "/usr/lib/python2.7/dist-packages/django/forms/forms.py" in _clean_form
296. self.cleaned_data = self.clean()
File "/home/sduckwo/projects/webhandin/webhandin/handin/forms.py" in clean
116. upload_to += self.cleaned_data['file'].name
Exception Type: KeyError at /courses/a/b/assignments/c/sduckwo/upload
Exception Value: 'file'
更新
从BucketUploadForm中删除clean方法会导致FileField的验证失败。但是,我需要使用clean方法进行其他检查,因此永久删除它不是一种选择。
我还发现通过修改clean方法看起来像这样:
class BucketUploadForm(forms.Form):
file = forms.FileField(required=True) # required=True is the default, but I'm being explicit
def clean(self):
if 'file' not in self.cleaned_data:
raise ValidationError('No file or empty file given')
upload_to = '/some/path'
upload_to += self.cleaned_data['file'].name # this is raising a KeyError
然后验证按预期失败,但我收到两条错误消息:
这告诉我FileField.clean()正在引发ValidationError,但该异常以某种方式被忽略,除非BucketUploadForm.clean()不存在或者引发ValidationError。
所以现在我可能更接近我的最终目标,但仍然非常困惑。
答案 0 :(得分:3)
它引发了关键错误,因为你必须首先调用super的clean方法才能设置self.cleaned_data
。
def clean(self):
super(BucketUploadForm, self).clean()
upload_to = '/some/path'
upload_to += self.cleaned_data['file'].name
你应该看一下Form and Field validation works的方式。如果您使用Django的表单,这是一个非常有启发性的读物。
希望这有帮助!
答案 1 :(得分:2)
我想我以前见过这个。从轶事经验看,如果字段丢失,Django似乎不会停止验证(例如,即使缺少必填字段,它也会调用clean())。它说,由于不包括cleaning_data中的字段名称,因此缺少必填字段。
斯科特指出它是documented here:对于任何字段,如果Field.clean()方法引发ValidationError,则不会调用任何特定于字段的清理方法。但是,仍会执行所有剩余字段的清理方法。
因此,我认为解决方案只是通过检查'file'键是否在cleaning_data中来防御性地编写clean()方法,如果不是,则按原样返回cleaning_data。 Django已经知道缺少必需的字段,因此is_valid()将失败并且不会造成任何伤害。
def clean(self):
upload_to = '/some/path'
if not 'file' in self.cleaned_data:
return self.cleaned_data
upload_to += self.cleaned_data['file'].name