What will happen
When you run form.is_valid() , the fields are checked and cleared one by one and stored in the variable cleaned_data . If you look at the source code for Django, you will see that the form fields are individually verified in the _clean_fields methods of the _clean_fields class in the django/forms/forms.py
The check is performed according to the type of widgets (i.e. forms.ClearableFileInput in case of the field you are interested in). After going a little deeper, you will see that cleaned_data populated with files.get(name) , where files is a list of updated files, and name is the name of the field being checked.
The type of files is MultiValueDict . If you look at the code in django/utils/datastructures.py , you will find interesting stuff around line 48. I will copy the docstring here:
A subclass of a dictionary configured to handle multiple values โโfor the same key.
>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']}) >>> d['name'] 'Simon' >>> d.getlist('name') ['Adrian', 'Simon'] >>> d.getlist('doesnotexist') [] >>> d.getlist('doesnotexist', ['Adrian', 'Simon']) ['Adrian', 'Simon'] >>> d.get('lastname', 'nonexistent') 'nonexistent' >>> d.setlist('lastname', ['Holovaty', 'Willison'])
This class exists to solve the annoyance problem caused by cgi.parse_qs, which returns a list for each key, even if most web forms submit single name-value pairs.
Since this behavior depends only on the field widget, now I see three different solutions.
Solutions
- You set Django to behave properly when the
attrs for the widget are set to multiple . (I was going to do it, but I'm really not sure about the consequences.) I will study this in depth and can give PR. - You create your own widget, the
ClearableFileInput , which override the value_from_datadict method to use files.getlist(name) instead of file.get(name) . - You are using
request.FILES.getlist('your_filed_name') as suggested by Astik Anand , or any easier solution.
Let's take a closer look at solution 2. Here are some instructions for creating your own widget based on ClearableFileInput . Unfortunately, this is not enough to make it work, because the data is sent through the cleaning process belonging to this field. You must also create your own FileField .
# widgets.py from django.forms.widgets import ClearableFileInput from django.forms.widgets import CheckboxInput FILE_INPUT_CONTRADICTION = object() class ClearableMultipleFilesInput(ClearableFileInput): def value_from_datadict(self, data, files, name): upload = files.getlist(name)
This part is mainly performed by the phrase from the ClearableFileInput methods, except for the first line value_from_datadict , which was upload = files.get(name) .
As mentioned earlier, you also need to create your own Field to override the to_python FileField method, which tries to access the self.name and self.size .
# fields.py from django.forms.fields import FileField from .widgets import ClearableMultipleFilesInput from .widgets import FILE_INPUT_CONTRADICTION class MultipleFilesField(FileField): widget = ClearableMultipleFilesInput def clean(self, data, initial=None):
And here is how to use it in your form:
# forms.py from .widgets import ClearableMultipleFilesInput from .fields import MultipleFilesField your_field = MultipleFilesField( widget=ClearableMultipleFilesInput( attrs={'multiple': True}))
And it works!
>>> print(form.cleaned_data['your_field'] [<TemporaryUploadedFile: file1.pdf (application/pdf)>, <TemporaryUploadedFile: file2.pdf (application/pdf)>, <TemporaryUploadedFile: file3.pdf (application/pdf)>]
Of course, this solution cannot be used directly and needs a lot of improvements. Here we basically delete all checks made in the FileField field, we do not set the maximum number of files, attrs={'multiple': True} redundant with the widget name and many similar things. Also, I'm sure I skipped some important methods in FileField or ClearableFileInput . This is only a starting idea, but you will need a lot more work and see widgets and fields in the official documentation.