Django:上传多个文件。 cleaning_data中需要的文件列表['文件']

时间:2017-09-20 09:43:00

标签: 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}))

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

3 个答案:

答案 0 :(得分:11)

会发生什么

运行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'])
  

这个类的存在是为了解决cgi.parse_qs引发的恼人问题,       即使大多数Web表单都提交,它也会返回每个键的列表       单个名称 - 值对。

由于此行为仅取决于该字段的小部件,我现在可以看到三种不同的解决方案。

解决方案

  1. 当窗口小部件的attrs设置为multiple时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我会深入研究,并可能提交公关。
  2. 您创建了自己的小工具,ClearableFileInput的子级,覆盖value_from_datadict方法以使用files.getlist(name)而不是file.get(name)
  3. 您使用Astik Anand建议的request.FILES.getlist('your_filed_name')或更简单的解决方案。
  4. 让我们仔细看看解决方案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_pythonFileField的{​​{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中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的widgetsfields

答案 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的多重属性。

相关问题