I followed the pattern of the docs, to upload several files with one forms.FileField
:
我按照文档的模式,上传了几个带有一个form.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.
不幸的是,cleaning_data ['file']确实包含一个文件,而不是两个文件。
What needs to be done to have all uploaded files on cleaned_data['file']
?
要将所有上传的文件都放在cleaning_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
有一个拉取请求来解决这个问题:https://github.com/django/django/pull/9011
3 个解决方案
#1
10
What happens
When your run form.is_valid()
, the fields are validated and cleaned one after one, and stored in the cleaned_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 class BaseForm
in the file django/forms/forms.py
运行form.is_valid()时,将逐个验证和清除字段,并将其存储在cleaning_data变量中。如果您查看Django源代码,您会发现您的表单字段在文件django / forms / forms.py中的类BaseForm的_clean_fields方法中进行单独验证。
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 the cleaned_data
is filled with files.get(name)
where files
is the list of the updated files, and name
is the name of the field currently being validated.
验证是根据窗口小部件类型进行的(即在您感兴趣的字段的情况下为forms.ClearableFileInput)。更深入一点,向您显示cleaning_data填充了files.get(name),其中files是更新文件的列表,name是当前正在验证的字段的名称。
The type of files
is MultiValueDict
. If you look at the code in django/utils/datastructures.py
, you'll find some interesting stuff around the line 48. I copy the docstring here :
文件类型是MultiValueDict。如果你看一下django / utils / datastructures.py中的代码,你会在第48行找到一些有趣的东西。我在这里复制docstring:
A subclass of dictionary customized 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 irritating problem raised by cgi.parse_qs, which returns a list for every key, even though most Web forms submit single name-value pairs.
这个类的存在是为了解决cgi.parse_qs引发的恼人问题,它返回每个键的列表,即使大多数Web表单提交单个名称 - 值对。
As this behavior depends only on the widget of the field, I can see three different solutions from now.
由于此行为仅取决于该字段的小部件,我现在可以看到三种不同的解决方案。
The solutions
- You patch Django to have a correct behavior when the
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. - You create your own Widget, a children of
ClearableFileInput
, which override thevalue_from_datadict
method to usefiles.getlist(name)
instead offile.get(name)
. - You use
request.FILES.getlist('your_filed_name')
as suggested by Astik Anand, or any easier solution.
当窗口小部件的attrs设置为多个时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我将深入研究,并可能提交PR。
您可以创建自己的Widget,它是ClearableFileInput的子项,它覆盖value_from_datadict方法以使用files.getlist(name)而不是file.get(name)。
您可以使用Astik Anand建议的request.FILES.getlist('your_filed_name'),或任何更简单的解决方案。
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 own FileField
as well.
让我们仔细看看解决方案2.下面是一些基于ClearableFileInput创建自己的小部件的说明。不幸的是,它还不足以使其工作,因为数据是通过现场拥有的清洁过程发送的。您还必须创建自己的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) # files.get(name) in Django source
if not self.is_required and CheckboxInput().value_from_datadict(
data, files, self.clear_checkbox_name(name)):
if upload:
# If the user contradicts themselves (uploads a new file AND
# checks the "clear" checkbox), we return a unique marker
# objects that FileField will turn into a ValidationError.
return FILE_INPUT_CONTRADICTION
# False signals to clear any existing value, as opposed to just None
return False
return upload
This part is basically taken word by word from the methods of ClearableFileInput
, except the first line of value_from_datadict
which was upload = files.get(name)
.
这部分基本上是从ClearableFileInput的方法逐字逐句采用的,除了value_from_datadict的第一行是upload = files.get(name)。
As mentioned before, you also have to create your own Field
to override the to_python
method of FileField
which tries to access a self.name
and self.size
attributes.
如前所述,您还必须创建自己的Field来覆盖FileField的to_python方法,该方法尝试访问self.name和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):
# If the widget got contradictory inputs, we raise a validation error
if data is FILE_INPUT_CONTRADICTION:
raise ValidationError(self.error_message['contradiction'], code='contradiction')
# False means the field value should be cleared; further validation is
# not needed.
if data is False:
if not self.required:
return False
# If the field is required, clearing is not possible (the widg et
# shouldn't return False data in that case anyway). False is not
# in self.empty_value; if a False value makes it this far
# it should be validated from here on out as None (so it will be
# caught by the required check).
data = None
if not data and initial:
return initial
return data
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 erase all the checking made in the FileField
field, we do not set a maximum number of files, the attrs={'multiple': True}
is redundant with the widget name, and many similar things. As well, I am pretty sure I missed some important methods in the FileField
or ClearableFileInput
. 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.
当然,这个解决方案不能直接使用,需要很多改进。在这里,我们基本上擦除了在FileField字段中进行的所有检查,我们没有设置最大文件数,attrs = {'multiple':True}对于小部件名称和许多类似的东西是多余的。同样,我很确定我错过了FileField或ClearableFileInput中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件和字段。
#2
4
I assume that you have:
我假设你有:
class FileFieldForm(forms.Form):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
and you are trying to get files
using : cleaned_data['files']
and you are getting only 1 file instead of 2.
并且您正在尝试使用:cleaning_data ['files']获取文件,而您只获得1个文件而不是2个文件。
The Reason:
What is happening here is, When you try to do something like this
这里发生的是,当你尝试做这样的事情
file in self.cleaned_data['files]:,
thinking that, you can iterate over a list of uploadedFile objects and pass each to the handler function.
想一想,你可以遍历一个uploadedFile对象列表并将每个对象传递给处理函数。
But cleaned_data['files']
is not a list for you, it's just ONE single instance of uploadedfile.
但clean_data ['files']不是你的列表,它只是上传文件的一个单一实例。
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).
迭代文件对象时,实际上是在阅读它。因此,最终传递给处理函数的不是文件对象,而是其内容(作为字节字符串)。
The solution
You need to get a list of files and then, perform something what you want on them as below.
您需要获取文件列表,然后按照以下方式执行您想要的操作。
files = request.FILES.getlist('files')
for f in files:
... # Do something with each file considering f as file object
#3
0
You can use this library: https://github.com/Chive/django-multiupload
您可以使用此库:https://github.com/Chive/django-multiupload
Django Multiupload
Dead simple drop-in multi file upload field for django forms using HTML5's multiple attribute.
使用HTML5的多重属性为django表单提供简单的简单嵌入式多文件上载字段。
#1
10
What happens
When your run form.is_valid()
, the fields are validated and cleaned one after one, and stored in the cleaned_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 class BaseForm
in the file django/forms/forms.py
运行form.is_valid()时,将逐个验证和清除字段,并将其存储在cleaning_data变量中。如果您查看Django源代码,您会发现您的表单字段在文件django / forms / forms.py中的类BaseForm的_clean_fields方法中进行单独验证。
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 the cleaned_data
is filled with files.get(name)
where files
is the list of the updated files, and name
is the name of the field currently being validated.
验证是根据窗口小部件类型进行的(即在您感兴趣的字段的情况下为forms.ClearableFileInput)。更深入一点,向您显示cleaning_data填充了files.get(name),其中files是更新文件的列表,name是当前正在验证的字段的名称。
The type of files
is MultiValueDict
. If you look at the code in django/utils/datastructures.py
, you'll find some interesting stuff around the line 48. I copy the docstring here :
文件类型是MultiValueDict。如果你看一下django / utils / datastructures.py中的代码,你会在第48行找到一些有趣的东西。我在这里复制docstring:
A subclass of dictionary customized 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 irritating problem raised by cgi.parse_qs, which returns a list for every key, even though most Web forms submit single name-value pairs.
这个类的存在是为了解决cgi.parse_qs引发的恼人问题,它返回每个键的列表,即使大多数Web表单提交单个名称 - 值对。
As this behavior depends only on the widget of the field, I can see three different solutions from now.
由于此行为仅取决于该字段的小部件,我现在可以看到三种不同的解决方案。
The solutions
- You patch Django to have a correct behavior when the
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. - You create your own Widget, a children of
ClearableFileInput
, which override thevalue_from_datadict
method to usefiles.getlist(name)
instead offile.get(name)
. - You use
request.FILES.getlist('your_filed_name')
as suggested by Astik Anand, or any easier solution.
当窗口小部件的attrs设置为多个时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我将深入研究,并可能提交PR。
您可以创建自己的Widget,它是ClearableFileInput的子项,它覆盖value_from_datadict方法以使用files.getlist(name)而不是file.get(name)。
您可以使用Astik Anand建议的request.FILES.getlist('your_filed_name'),或任何更简单的解决方案。
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 own FileField
as well.
让我们仔细看看解决方案2.下面是一些基于ClearableFileInput创建自己的小部件的说明。不幸的是,它还不足以使其工作,因为数据是通过现场拥有的清洁过程发送的。您还必须创建自己的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) # files.get(name) in Django source
if not self.is_required and CheckboxInput().value_from_datadict(
data, files, self.clear_checkbox_name(name)):
if upload:
# If the user contradicts themselves (uploads a new file AND
# checks the "clear" checkbox), we return a unique marker
# objects that FileField will turn into a ValidationError.
return FILE_INPUT_CONTRADICTION
# False signals to clear any existing value, as opposed to just None
return False
return upload
This part is basically taken word by word from the methods of ClearableFileInput
, except the first line of value_from_datadict
which was upload = files.get(name)
.
这部分基本上是从ClearableFileInput的方法逐字逐句采用的,除了value_from_datadict的第一行是upload = files.get(name)。
As mentioned before, you also have to create your own Field
to override the to_python
method of FileField
which tries to access a self.name
and self.size
attributes.
如前所述,您还必须创建自己的Field来覆盖FileField的to_python方法,该方法尝试访问self.name和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):
# If the widget got contradictory inputs, we raise a validation error
if data is FILE_INPUT_CONTRADICTION:
raise ValidationError(self.error_message['contradiction'], code='contradiction')
# False means the field value should be cleared; further validation is
# not needed.
if data is False:
if not self.required:
return False
# If the field is required, clearing is not possible (the widg et
# shouldn't return False data in that case anyway). False is not
# in self.empty_value; if a False value makes it this far
# it should be validated from here on out as None (so it will be
# caught by the required check).
data = None
if not data and initial:
return initial
return data
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 erase all the checking made in the FileField
field, we do not set a maximum number of files, the attrs={'multiple': True}
is redundant with the widget name, and many similar things. As well, I am pretty sure I missed some important methods in the FileField
or ClearableFileInput
. 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.
当然,这个解决方案不能直接使用,需要很多改进。在这里,我们基本上擦除了在FileField字段中进行的所有检查,我们没有设置最大文件数,attrs = {'multiple':True}对于小部件名称和许多类似的东西是多余的。同样,我很确定我错过了FileField或ClearableFileInput中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件和字段。
#2
4
I assume that you have:
我假设你有:
class FileFieldForm(forms.Form):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
and you are trying to get files
using : cleaned_data['files']
and you are getting only 1 file instead of 2.
并且您正在尝试使用:cleaning_data ['files']获取文件,而您只获得1个文件而不是2个文件。
The Reason:
What is happening here is, When you try to do something like this
这里发生的是,当你尝试做这样的事情
file in self.cleaned_data['files]:,
thinking that, you can iterate over a list of uploadedFile objects and pass each to the handler function.
想一想,你可以遍历一个uploadedFile对象列表并将每个对象传递给处理函数。
But cleaned_data['files']
is not a list for you, it's just ONE single instance of uploadedfile.
但clean_data ['files']不是你的列表,它只是上传文件的一个单一实例。
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).
迭代文件对象时,实际上是在阅读它。因此,最终传递给处理函数的不是文件对象,而是其内容(作为字节字符串)。
The solution
You need to get a list of files and then, perform something what you want on them as below.
您需要获取文件列表,然后按照以下方式执行您想要的操作。
files = request.FILES.getlist('files')
for f in files:
... # Do something with each file considering f as file object
#3
0
You can use this library: https://github.com/Chive/django-multiupload
您可以使用此库:https://github.com/Chive/django-multiupload
Django Multiupload
Dead simple drop-in multi file upload field for django forms using HTML5's multiple attribute.
使用HTML5的多重属性为django表单提供简单的简单嵌入式多文件上载字段。