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:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With