Django:上传多个文件。清空数据['文件']中需要的文件列表 [英] Django: Uploading multiple files. List of files needed in cleaned_data['file']

查看:254
本文介绍了Django:上传多个文件。清空数据['文件']中需要的文件列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遵循这个文档的模式,用一个 forms.FileField

上传几个文件。 a href =https://docs.djangoproject.com/en/1.11/topics/http/file-uploads/#uploading-multiple-files =nofollow noreferrer> https://docs.djangoproject.com/ zh / 1.11 / topics / http / file-uploads /#上传 - 多个文件



不幸的是 cleared_data ['file'] 确实包含一个文件,而不是两个文件。



需要做什么才能将所有上传的文件放在 cleared_data [ 'file']



以下是文档中的代码:

form.py

  from django import forms 

class FileFieldForm(forms .Form):
file_field = forms.FileField(widget = forms.ClearableFileInput(attrs = {'multiple':True}))

$来自django.views.generic的b
$ b

views.py

  .edit从.forms中导入FormView 
导入FileFieldForm
$ b $ class FileFieldView(FormView):
form_class = FileFieldForm $ b $ template_name ='upload.html'#用您的模板替换。
success_url ='...'#用您的网址或反向()替换。
$ b def post(self,request,* args,** kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
如果form.is_valid():
用于文件中的f:
...#对每个文件执行一些操作。
return self.form_valid(form)
else:
return self.form_invalid(form)



更新



解决此问题有一个拉取请求: https://github.com/django/django/pull/9011

form.is_valid()时,会发生什么情况?

,这些字段会被一一验证和清理,并存储在 cleared_data 变量中。如果你看Django的源代码,你会发现你的表单字段会经过类 BaseForm的 _clean_fields / code>在文件中> django / forms / forms.py



验证是根据窗口部件类型(如果您感兴趣的领域是 ie forms.ClearableFileInput )。进一步说明 cleared_data 是用 files.get(name)填充的,其中 files 是更新文件的列表, name 是当前正在验证的字段的名称。



files 的类型是 MultiValueDict 。如果你看看 django / utils / datastructures.py 中的代码,你会在第48行找到一些有趣的东西。我在这里复制文档字符串:


$ b


字典的一个子类,用于处理
同一个键的多个值。



 >>> 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表单提交了
单个名称/值对,也会返回每个键的列表。


由于这种行为仅依赖于字段的小部件,所以从现在开始我可以看到三种不同的解决方案。



解决方案




  1. 当Django在 attrs 设置为 multiple 。 (我即将这样做,但我不确定后果。)我会深入研究并提交PR。

  2. 创建自己的Widget, (> ClearableFileInput )的子元素,它重写 value_from_datadict 方法以使用 files.getlist(name) 代替 file.get(name)

  3. 使用请求。按 Astik Anand 建议的FILES.getlist('your_filed_name'),或者更简单解决方案。

让我们仔细看看解决方案2.
下面是一些基于 ClearableFileInput 。不幸的是,数据是通过现场所有的清洁过程发送的,所以仅仅使它工作是不够的。您必须创建您自己的 FileField

 #widgets.py 
from django.forms.widgets从django.forms.widgets中导入ClearableFileInput
import CheckboxInput
$ b FILE_INPUT_CONTRADICTION = object()
$ b $ class ClearableMultipleFilesInput(ClearableFileInput) :
def value_from_datadict(self,data,files,name):
upload = files.getlist(name)Django源文件中的files.get(name)

如果不是self (
数据,文件,self.clear_checkbox_name(name)):

如果上传:
#如果用户矛盾(上传一个新的文件AND
#选中清除复选框),我们返回一个唯一标记
#对象,FileField将变成一个ValidationError。
返回FILE_INPUT_CONTRADICTION
#假信号清除任何现有的值,而不仅仅是无
返回假
返回上传
$ b

这部分基本上是从 ClearableFileInput 的方法逐字进行的,除了第一行 value_from_datadict 它是 upload = files.get(name)



如前所述,您还必须创建自己的 Field 来覆盖 to_python 方法。试图访问 self.name self.size 属性的FileField p>

 #fields.py从django.forms.fields获取b $ b从.widgets导入FileField 
import ClearableMultipleFilesInput
从.widgets导入FILE_INPUT_CONTRADICTION

类MultipleFilesField(FileField):
widget = ClearableMultipleFilesInput

def clean(self,dat如果数据是FILE_INPUT_CONTRADICTION:
raise ValidationError(self.error_message ['contradiction'],code ='矛盾')
#False表示字段值应该被清除;进一步的验证是
#不需要。
如果数据是假的:
如果不是self.required:
return False
#如果该字段是必需的,清除是不可能的(widg et
#shouldn在这种情况下,不会返回错误的数据)。 False不是
#in self.empty_value;如果一个False值使得它到目前为止
#它应该从这里验证为None(所以它将被
#所需的检查捕获)。
data = None
如果不是数据和初始值:
返回初始
返回数据

以下是如何在你的表单中使用它:

 #forms.py $ b $从.widgets导入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} 与widget的名字是多余的,还有许多类似的东西。我也很确定我错过了 FileField code>或 ClearableFileInput 这只是一个开始的想法,但是您需要更多的工作,并且看一看小部件字段重刑。


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 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

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.

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 :

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.

As this behavior depends only on the widget of the field, I can see three different solutions from now.

The solutions

  1. You patch Django to have a correct behavior when the attrs of the widget is set to multiple. (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.
  2. You create your own Widget, a children of ClearableFileInput, which override the value_from_datadict method to use files.getlist(name) instead of file.get(name).
  3. You use 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 own FileField as well.

# 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).

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.

# 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.

这篇关于Django:上传多个文件。清空数据['文件']中需要的文件列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆