I followed the pattern of the docs, to upload several files with one forms.FileField
:
https://docs.djangoproject.com/en/1.11/topics/http/file-uploads/#uploading-multiple-files
Unfortunately cleaned_data['file']
does contain one file, not both files.
What needs to be done to have all uploaded files on cleaned_data['file']
?
Here is the code from the docs:
forms.py
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
views.py
from django.views.generic.edit import FormView
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
... # Do something with each file.
return self.form_valid(form)
else:
return self.form_invalid(form)
Update
There is a pull request to solve this issue: https://github.com/django/django/pull/9011
What happens
When your run
form.is_valid()
, the fields are validated and cleaned one after one, and stored in thecleaned_data
variable. If you look at the Django source code, you'll find that your form fields go through an individual validation in the_clean_fields
methods of the classBaseForm
in the filedjango/forms/forms.py
The validation is made according to the widget type (ie
forms.ClearableFileInput
in the case of the field you are interested in). Going a bit deeper shows you that thecleaned_data
is filled withfiles.get(name)
wherefiles
is the list of the updated files, andname
is the name of the field currently being validated.The type of
files
isMultiValueDict
. If you look at the code indjango/utils/datastructures.py
, you'll find some interesting stuff around the line 48. I copy the docstring here :As this behavior depends only on the widget of the field, I can see three different solutions from now.
The solutions
attrs
of the widget is set tomultiple
. (I was about to do it, but I'm really not sure about the consequences.) I'll study that in depth and may submit a PR.ClearableFileInput
, which override thevalue_from_datadict
method to usefiles.getlist(name)
instead offile.get(name)
.request.FILES.getlist('your_filed_name')
as suggested by Astik Anand, or any easier solution.Let's take a closer look at the solution 2. Here are some instructions to create your own widget based on
ClearableFileInput
. Unfortunately, it is not enough to make it work, as the data are sent through a cleaning process owned by the field. You must create your ownFileField
as well.This part is basically taken word by word from the methods of
ClearableFileInput
, except the first line ofvalue_from_datadict
which wasupload = files.get(name)
.As mentioned before, you also have to create your own
Field
to override theto_python
method ofFileField
which tries to access aself.name
andself.size
attributes.And here is how to use it in your form:
And it works!
Of course, this solution cannot be used directly and needs a lot of improvements. Here, we basically erase all the checking made in the
FileField
field, we do not set a maximum number of files, theattrs={'multiple': True}
is redundant with the widget name, and many similar things. As well, I am pretty sure I missed some important methods in theFileField
orClearableFileInput
. This is only a starting idea, but you'll need much more work, and a look at the widgets and fields on the official documentation.You can use this library: https://github.com/Chive/django-multiupload
I assume that you have:
and you are trying to get
files
using :cleaned_data['files']
and you are getting only 1 file instead of 2.What is happening here is, When you try to do something like this
thinking that, you can iterate over a list of uploadedFile objects and pass each to the handler function.
But
cleaned_data['files']
is not a list for you, it's just ONE single instance of uploadedfile.When you iterate over a file object, you're actually reading it. So what you pass eventually to the handler function is not the file object but its content (as a bytes string).
You need to get a list of files and then, perform something what you want on them as below.