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

时间:2021-10-20 06:31:44

I followed the pattern of the docs, to upload several files with one forms.FileField:

我按照文档的模式,上传了几个带有一个form.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.

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

What needs to be done to have all uploaded files on cleaned_data['file']?

要将所有上传的文件都放在cleaning_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

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

3 个解决方案

#1


10  

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

运行form.is_valid()时,将逐个验证和清除字段,并将其存储在cleaning_data变量中。如果您查看Django源代码,您会发现您的表单字段在文件django / forms / forms.py中的类BaseForm的_clean_fields方法中进行单独验证。

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.

验证是根据窗口小部件类型进行的(即在您感兴趣的字段的情况下为forms.ClearableFileInput)。更深入一点,向您显示cleaning_data填充了files.get(name),其中files是更新文件的列表,name是当前正在验证的字段的名称。

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 :

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

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.

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

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. 当窗口小部件的attrs设置为多个时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我将深入研究,并可能提交PR。

  3. 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).
  4. 您可以创建自己的Widget,它是ClearableFileInput的子项,它覆盖value_from_datadict方法以使用files.getlist(name)而不是file.get(name)。

  5. You use request.FILES.getlist('your_filed_name') as suggested by Astik Anand, or any easier solution.
  6. 您可以使用Astik Anand建议的request.FILES.getlist('your_filed_name'),或任何更简单的解决方案。

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.

让我们仔细看看解决方案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

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

这部分基本上是从ClearableFileInput的方法逐字逐句采用的,除了value_from_datadict的第一行是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.

如前所述,您还必须创建自己的Field来覆盖FileField的to_python方法,该方法尝试访问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

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.

当然,这个解决方案不能直接使用,需要很多改进。在这里,我们基本上擦除了在FileField字段中进行的所有检查,我们没有设置最大文件数,attrs = {'multiple':True}对于小部件名称和许多类似的东西是多余的。同样,我很确定我错过了FileField或ClearableFileInput中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件和字段。

#2


4  

I assume that you have:

我假设你有:

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

and you are trying to get files using : cleaned_data['files'] and you are getting only 1 file instead of 2.

并且您正在尝试使用:cleaning_data ['files']获取文件,而您只获得1个文件而不是2个文件。

The Reason:

What is happening here is, When you try to do something like this

这里发生的是,当你尝试做这样的事情

file in self.cleaned_data['files]:, 

thinking that, you can iterate over a list of uploadedFile objects and pass each to the handler function.

想一想,你可以遍历一个uploadedFile对象列表并将每个对象传递给处理函数。

But cleaned_data['files'] is not a list for you, it's just ONE single instance of uploadedfile.

但clean_data ['files']不是你的列表,它只是上传文件的一个单一实例。

When you iterate over a file object, you're actually reading it. So what you pass eventually to the handler function is not the file object but its content (as a bytes string).

迭代文件对象时,实际上是在阅读它。因此,最终传递给处理函数的不是文件对象,而是其内容(作为字节字符串)。

The solution

You need to get a list of files and then, perform something what you want on them as below.

您需要获取文件列表,然后按照以下方式执行您想要的操作。

files = request.FILES.getlist('files')

for f in files:
    ...  # Do something with each file considering f as file object

#3


0  

You can use this library: https://github.com/Chive/django-multiupload

您可以使用此库:https://github.com/Chive/django-multiupload

Django Multiupload

Dead simple drop-in multi file upload field for django forms using HTML5's multiple attribute.

使用HTML5的多重属性为django表单提供简单的简单嵌入式多文件上载字段。

#1


10  

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

运行form.is_valid()时,将逐个验证和清除字段,并将其存储在cleaning_data变量中。如果您查看Django源代码,您会发现您的表单字段在文件django / forms / forms.py中的类BaseForm的_clean_fields方法中进行单独验证。

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.

验证是根据窗口小部件类型进行的(即在您感兴趣的字段的情况下为forms.ClearableFileInput)。更深入一点,向您显示cleaning_data填充了files.get(name),其中files是更新文件的列表,name是当前正在验证的字段的名称。

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 :

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

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.

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

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. 当窗口小部件的attrs设置为多个时,您可以修补Django以获得正确的行为。 (我即将这样做,但我真的不确定后果。)我将深入研究,并可能提交PR。

  3. 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).
  4. 您可以创建自己的Widget,它是ClearableFileInput的子项,它覆盖value_from_datadict方法以使用files.getlist(name)而不是file.get(name)。

  5. You use request.FILES.getlist('your_filed_name') as suggested by Astik Anand, or any easier solution.
  6. 您可以使用Astik Anand建议的request.FILES.getlist('your_filed_name'),或任何更简单的解决方案。

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.

让我们仔细看看解决方案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

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

这部分基本上是从ClearableFileInput的方法逐字逐句采用的,除了value_from_datadict的第一行是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.

如前所述,您还必须创建自己的Field来覆盖FileField的to_python方法,该方法尝试访问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

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.

当然,这个解决方案不能直接使用,需要很多改进。在这里,我们基本上擦除了在FileField字段中进行的所有检查,我们没有设置最大文件数,attrs = {'multiple':True}对于小部件名称和许多类似的东西是多余的。同样,我很确定我错过了FileField或ClearableFileInput中的一些重要方法。这只是一个开始的想法,但您需要做更多的工作,并查看官方文档中的小部件和字段。

#2


4  

I assume that you have:

我假设你有:

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

and you are trying to get files using : cleaned_data['files'] and you are getting only 1 file instead of 2.

并且您正在尝试使用:cleaning_data ['files']获取文件,而您只获得1个文件而不是2个文件。

The Reason:

What is happening here is, When you try to do something like this

这里发生的是,当你尝试做这样的事情

file in self.cleaned_data['files]:, 

thinking that, you can iterate over a list of uploadedFile objects and pass each to the handler function.

想一想,你可以遍历一个uploadedFile对象列表并将每个对象传递给处理函数。

But cleaned_data['files'] is not a list for you, it's just ONE single instance of uploadedfile.

但clean_data ['files']不是你的列表,它只是上传文件的一个单一实例。

When you iterate over a file object, you're actually reading it. So what you pass eventually to the handler function is not the file object but its content (as a bytes string).

迭代文件对象时,实际上是在阅读它。因此,最终传递给处理函数的不是文件对象,而是其内容(作为字节字符串)。

The solution

You need to get a list of files and then, perform something what you want on them as below.

您需要获取文件列表,然后按照以下方式执行您想要的操作。

files = request.FILES.getlist('files')

for f in files:
    ...  # Do something with each file considering f as file object

#3


0  

You can use this library: https://github.com/Chive/django-multiupload

您可以使用此库:https://github.com/Chive/django-multiupload

Django Multiupload

Dead simple drop-in multi file upload field for django forms using HTML5's multiple attribute.

使用HTML5的多重属性为django表单提供简单的简单嵌入式多文件上载字段。