【问题标题】:Django forms.FileField(required=True) is passing validation if emptyDjango forms.FileField(required=True) 如果为空则通过验证
【发布时间】:2013-07-09 21:35:57
【问题描述】:

我有一个带有必需 FileField 的 Django 表单(不是 ModelForm)。根据the FileField documentation,验证 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() 提出,这是我最初所追求的。

这告诉我 FileField.clean() 正在引发 ValidationError,但该异常会以某种方式被忽略,除非 BucketUploadForm.clean() 不存在或也引发 ValidationError。

所以现在我可能离最终目标更近了一点,但仍然很困惑。

【问题讨论】:

    标签: django django-forms


    【解决方案1】:

    我想我以前见过这个。从轶事经验来看,如果缺少字段,Django 似乎不会停止验证(例如,即使缺少必填字段,它也会调用 clean())。它说由于在cleaned_data中不包含字段名称而缺少必填字段。

    Scott 指出是documented here

    对于任何字段,如果 Field.clean() 方法引发 ValidationError,则不会调用任何特定于字段的清理方法。但是,仍然会执行所有剩余字段的清理方法。

    因此,我认为解决方案只是通过检查“文件”键是否在cleaned_data 中来防御性地对您的clean() 方法进行编程,如果不是,则按原样返回cleaned_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
    

    【讨论】:

      【解决方案2】:

      它会引发关键错误,因为您必须先调用 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 的表单,这是一本非常有启发性的读物。

      希望这会有所帮助!

      【讨论】:

      • 我忘了说我已经尝试过调用 super 的 clean()。做你建议的没有帮助。另请注意,表单和字段验证文档中的示例都没有调用 super 的 clean() 方法。
      • @ScottDuckworth:你的代码看起来不错,你有仔细检查缩进吗?除此之外我没有任何线索。
      猜你喜欢
      • 2012-04-20
      • 1970-01-01
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-09
      相关资源
      最近更新 更多