Django forms.FileField(required = True)如果为空则传递验证

时间:2013-07-11 14:38:53

标签: django django-forms

我有一个带有所需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

然后验证按预期失败,但我收到两条错误消息:

  1. '没有给出的文件或空文件',由BucketUploadForm.clean()提出
  2. '这个字段是必需的',由FileField.clean()引发,这是我在第一时间所追求的。
  3. 这告诉我FileField.clean()正在引发ValidationError,但该异常以某种方式被忽略,除非BucketUploadForm.clean()不存在或者引发ValidationError。

    所以现在我可能更接近我的最终目标,但仍然非常困惑。

2 个答案:

答案 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