gue*_*tli 9 django file-upload
我按照文档的模式,上传了几个文件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}))
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
更新
有一个拉取请求来解决这个问题:https://github.com/django/django/pull/9011
Dun*_*tos 14
运行时form.is_valid(),将逐个验证和清除字段,并将其存储在cleaned_data变量中.如果您查看Django源代码,您会发现您的表单字段在文件_clean_fields中的类的方法中进行单独验证BaseFormdjango/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'])
Run Code Online (Sandbox Code Playgroud)
这个类的存在是为了解决cgi.parse_qs引发的恼人问题,它为每个键返回一个列表,即使大多数Web表单提交单个名称 - 值对.
由于此行为仅取决于字段的小部件,因此我可以看到三种不同的解决方案.
attrs窗口小部件设置为时,您可以修补Django以获得正确的行为multiple.(我即将这样做,但我真的不确定后果.)我会深入研究,并可能提交PR.ClearableFileInput,它覆盖了value_from_datadict要使用的方法files.getlist(name)而不是file.get(name).request.FILES.getlist('your_filed_name')按照Astik Anand的建议使用,或任何更简单的解决方案.让我们仔细看看解决方案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
Run Code Online (Sandbox Code Playgroud)
这部分基本上是从方法中逐字逐句采用的ClearableFileInput,除了第一行value_from_datadict是upload = files.get(name).
如前所述,您还必须创建自己Field的to_python方法来覆盖FileField尝试访问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
Run Code Online (Sandbox Code Playgroud)
以下是如何在您的表单中使用它:
# forms.py
from .widgets import ClearableMultipleFilesInput
from .fields import MultipleFilesField
your_field = MultipleFilesField(
widget=ClearableMultipleFilesInput(
attrs={'multiple': True}))
Run Code Online (Sandbox Code Playgroud)
它的工作原理!
>>> print(form.cleaned_data['your_field']
[<TemporaryUploadedFile: file1.pdf (application/pdf)>, <TemporaryUploadedFile: file2.pdf (application/pdf)>, <TemporaryUploadedFile: file3.pdf (application/pdf)>]
Run Code Online (Sandbox Code Playgroud)
当然,这个解决方案不能直接使用,需要很多改进.在这里,我们基本上擦除了在FileField字段中进行的所有检查,我们没有设置最大文件数,attrs={'multiple': True}与窗口小部件名称是多余的,以及许多类似的东西.同时,我敢肯定我错过了一些重要的方法FileField或ClearableFileInput.这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件和字段.
我假设你有:
class FileFieldForm(forms.Form):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
Run Code Online (Sandbox Code Playgroud)
并且您正在尝试files使用 :cleaned_data['files']并且您只获得 1 个文件而不是 2 个。
原因:
这里发生的事情是,当你尝试做这样的事情时
file in self.cleaned_data['files]:,
Run Code Online (Sandbox Code Playgroud)
考虑到这一点,您可以遍历 UploadFile 对象列表并将每个对象传递给处理程序函数。
但cleaned_data['files']它不是适合您的列表,它只是上传文件的一个实例。
当您迭代文件对象时,您实际上是在读取它。所以你最终传递给处理函数的不是文件对象,而是它的内容(作为字节字符串)。
解决方案
您需要获取文件列表,然后对它们执行您想要的操作,如下所示。
files = request.FILES.getlist('files')
for f in files:
... # Do something with each file considering f as file object
Run Code Online (Sandbox Code Playgroud)