Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have a Django Form (not a ModelForm) with a required FileField. According to the FileField documentation, validation of the FileField should validate that non-empty file data has been bound to the form, but I am not seeing this behavior. Instead I am able to submit the form without a file and the form passes validation. The expected behavior would be to fail validation.

Things work as expected when a file is specified in the form.

My form looks something like this:

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

My view looks something like this:

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))

My template looks something like this:

<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Upload" />
</form>

The traceback looks like this:

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'

UPDATE

Removing the clean method from BucketUploadForm causes the FileField's validation to fail as expected. However, I need the clean method for other checks, so removing it permanently is not an option.

I've also found that by modifying the clean method to look like this:

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

then validation fails as expected, but I get two error messages:

  1. 'No file or empty file given', raised by BucketUploadForm.clean()
  2. 'This field is required', raised by FileField.clean(), which is what I was after in the first place.

This tells me that FileField.clean() is raising a ValidationError, but that exception is somehow being ignored unless BucketUploadForm.clean() either does not exist or also raises a ValidationError.

So now I'm maybe a little closer to my end goal but still very confused.

share|improve this question

2 Answers

up vote 1 down vote accepted

I think I've seen this before. From anecdotal experience it seems like Django does not stop validation if a field is missing (e.g. it will call clean() even if missing required fields). It says a required field is missing by not including the field name in cleaned_data.

Scott pointed out it is documented here:

For any field, if the Field.clean() method raises a ValidationError, any field-specific cleaning method is not called. However, the cleaning methods for all remaining fields are still executed.

Therefore, I think the solution is just to program your clean() method defensively by checking if the 'file' key is in cleaned_data and if it isn't, just return cleaned_data as is. Django already knows the required field is missing, so is_valid() will fail and no harm will come.

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
share|improve this answer

It raises key error because you have to call super's clean method first in order to get self.cleaned_data set.

def clean(self):
    super(BucketUploadForm, self).clean()
    upload_to = '/some/path'
    upload_to += self.cleaned_data['file'].name

You should take a look at how Form and Field validation works. It is a very enlightening read if you use to work with Django's Forms.

Hope this helps!

share|improve this answer
 
I forgot to mention that I had already tried calling super's clean(). Doing what you suggested doesn't help. Also notice that none of the examples in the form and field validation documentation call super's clean() method. –  Scott Duckworth Jul 11 at 17:00
 
@ScottDuckworth: your code seems ok, have you double-checked indentation? Besides that I don't have any clue. –  Paulo Bu Jul 11 at 17:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.