我按照文档的模式,使用一个forms.FileField
上传了几个文件:
https://docs.djangoproject.com/en/1.11/topics/http/file-uploads/#uploading-multiple-files
不幸的是cleaned_data['file']
确实包含一个文件,而不是两个文件。
在cleaned_data['file']
上添加所有文件需要做些什么?
以下是文档中的代码:
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)
更新
有解决此问题的拉取请求:https://github.com/django/django/pull/9011
答案 0 :(得分:11)
运行form.is_valid()
时,字段会逐一验证并清除,并存储在cleaned_data
变量中。如果您查看Django源代码,您会发现您的表单字段在文件_clean_fields
中的BaseForm
类django/forms/forms.py
方法中进行单独验证
根据窗口小部件类型进行验证(对于您感兴趣的字段,即 forms.ClearableFileInput
)。更深入地向您显示cleaned_data
已填充files.get(name)
,其中files
是更新文件的列表,name
是当前正在验证的字段的名称
files
的类型为MultiValueDict
。如果你查看django/utils/datastructures.py
中的代码,你会在第48行找到一些有趣的东西。我在这里复制文档字符串:
自定义的字典子类,用于处理多个值 同样的关键。
>>> 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'])
这个类的存在是为了解决cgi.parse_qs引发的恼人问题, 即使大多数Web表单都提交,它也会返回每个键的列表 单个名称 - 值对。
由于此行为仅取决于该字段的小部件,我现在可以看到三种不同的解决方案。
attrs
设置为multiple
时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我会深入研究,并可能提交公关。ClearableFileInput
的子级,覆盖value_from_datadict
方法以使用files.getlist(name)
而不是file.get(name)
。request.FILES.getlist('your_filed_name')
或更简单的解决方案。让我们仔细看看解决方案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
这一部分基本上是从ClearableFileInput
的方法逐字逐句采用的,但value_from_datadict
的第一行是upload = files.get(name)
。
如前所述,您还必须创建自己的Field
以覆盖试图访问to_python
和FileField
的{{1}} 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
它有效!
# forms.py
from .widgets import ClearableMultipleFilesInput
from .fields import MultipleFilesField
your_field = MultipleFilesField(
widget=ClearableMultipleFilesInput(
attrs={'multiple': True}))
当然,此解决方案无法直接使用,需要进行大量改进。在这里,我们基本上擦除了>>> print(form.cleaned_data['your_field']
[<TemporaryUploadedFile: file1.pdf (application/pdf)>, <TemporaryUploadedFile: file2.pdf (application/pdf)>, <TemporaryUploadedFile: file3.pdf (application/pdf)>]
字段中的所有检查,我们没有设置最大文件数,FileField
对于窗口小部件名称是多余的,以及许多类似的事情。同样,我很确定我错过了attrs={'multiple': True}
或FileField
中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的widgets和fields。
答案 1 :(得分:4)
我认为你有:
class FileFieldForm(forms.Form):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
并且您尝试使用files
cleaned_data['files']
并且您只获得1个文件而不是2个文件。
原因:
这里发生的事情是,当你尝试做这样的事情时
file in self.cleaned_data['files]:,
认为,您可以迭代uploadFile对象列表并将每个对象传递给处理函数。
但cleaned_data['files']
不是您的列表,它只是上传文件的一个单一实例。
当您遍历文件对象时,您实际上正在阅读它。因此,最终传递给处理函数的不是文件对象,而是其内容(作为字节字符串)。
解决方案
您需要获取文件列表,然后按照以下步骤执行您想要的操作。
files = request.FILES.getlist('files')
for f in files:
... # Do something with each file considering f as file object
答案 2 :(得分:0)
您可以使用此库:https://github.com/Chive/django-multiupload
Django Multiupload
使用django表单的死简单插件多文件上传字段 HTML5的多重属性。