Django:上传多个文件.cleaning_data ['file']中所需文件列表

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目前正在验证的字段的名称.

类型filesMultiValueDict.如果你查看代码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表单提交单个名称 - 值对.

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

解决方案

  1. attrs窗口小部件设置为时,您可以修补Django以获得正确的行为multiple.(我即将这样做,但我真的不确定后果.)我会深入研究,并可能提交PR.
  2. 您可以创建自己的Widget,它是一个子项ClearableFileInput,它覆盖了value_from_datadict要使用的方法files.getlist(name)而不是file.get(name).
  3. 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_datadictupload = files.get(name).

如前所述,您还必须创建自己Fieldto_python方法来覆盖FileField尝试访问self.nameself.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}与窗口小部件名称是多余的,以及许多类似的东西.同时,我敢肯定我错过了一些重要的方法FileFieldClearableFileInput.这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件字段.


Ast*_*and 7

我假设你有:

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)

  • 感谢您提供此解决方法。是的,这可行,但在我看来这是一种解决方法。表单可以有前缀。如果我在表单代码中使用 request.FILES.getlist('files')` ,则不会使用前缀...我喜欢从漂亮的 django 表单库中获得的“conerns 分离”。据我所知,这是我需要直接访问请求对象的唯一部分。我想避免这种情况。 (2认同)