【问题标题】:Proper Accessor for Django Uploaded FilesDjango 上传文件的正确访问器
【发布时间】:2011-03-21 23:17:43
【问题描述】:

假设 Django 表单如下...

class MyForm(forms.Form):
    attachment = forms.FileField()

我看过很多使用request.FILES['attachment'] 访问上传的Django 文件的教程,但我的印象是,只要有可能,您应该通过form.cleaned_data['attachment'] 访问POSTed 数据。

有人会使用request.FILES[] 有什么原因吗?这些对象是否应该包含完全相同的数据?

【问题讨论】:

    标签: python django django-forms http-post


    【解决方案1】:

    当您在 HTML 中创建表单时,它具有特定的编码(或将数据发送到服务器的方法)。默认情况下,编码是application/x-www-form-urlencoded,它本质上是在单个字符串中发送表单详细信息。但是,如果您想将文件上传到服务器,则需要将编码设置为multipart/form-data(这是您将在所有有关该主题的教程中注意到的enctype="..." 行)。这会以多个部分发送数据,每个表单字段一个。有关两种编码如何显示的示例,see here

    当 Django 遇到multipart/form-data 编码时,它会将接收到的数据拆分为两个字典:request.FILES 字典包含所有上传的文件,而request.POST 包含任何其他表单字段。如果有兴趣,处理由django/http/__init__.py文件中的MultiPartParser类完成。

    为了说明如何将这些数据呈现回您的代码,让我们创建一个简单的应用程序。首先,让我们做一个简单的表单,由一个字符字段和一个文件字段组成:

    from django import forms
    
    class TestForm(forms.Form):
        name = forms.CharField()
        file = forms.FileField()
    

    接下来,我们将创建一个简单的视图来创建一个表单,将任何提交的数据绑定到它,并通过模板呈现它:

    from django.shortcuts import render_to_response
    from django.template import RequestContext
    
    from forms import TestForm
    
    def show_form(request):
        if request.method == 'POST':
            form = TestForm(request.POST, request.FILES)
        else:
            form = TestForm()
    
        context = {
            'form': form
        }
        return render_to_response('show_form.html', context, RequestContext(request))
    

    最后,我们将使用模板来显示表单以及有关请求和表单的一些信息:

    <html>
        <head>
            <title>Django forms - file test</title>
        </head>
    
        <body>
            <form method="post" enctype="multipart/form-data">
                {% csrf_token %}
                {{ form.as_p }}
                <input type="submit" />
            </form>
    
            <h2>Request details</h2>
    
            <p>
            Request method: {{ request.method }}
            <br />
            POST data: {{ request.POST|default:"No data" }}
            <br />
            FILES data: {{ request.FILES|default:"No data" }}
            </p>
    
            <h2>Form details</h2>
    
            <p>
            Cleaned data: {{ form.cleaned_data|default:"No data" }}
            </p>
    
        </body>
    </html>
    

    请注意,您需要在设置中启用 django.core.context_processors.request 上下文处理器才能查看有关请求的详细信息。

    如果我们随后启动服务器并将浏览器指向视图,我们会看到我们期望看到的内容 - 一个空表单,请求模式是 GET,并且没有 POST、FILES 或表单数据。

    接下来,在字符字段中输入名称,但不要选择要上传的文件。提交后,我们会收到有关所需文件字段的预期错误。我们更感兴趣的是有关请求的信息:


    请求详情

    请求方式:POST
    POST 数据:
    文件数据:无数据

    表格详情

    已清理数据:无数据


    由于浏览器没有发送文件信息,Django 已将所有表单详细信息放入 POST 字典中,并将 FILES 字典留空。由于表单无效,因此没有与之关联的数据。

    现在让我们尝试不带名称,但要上传文件:


    请求详情

    请求方式:POST
    POST 数据:
    文件数据:]}>

    表格详情

    已清理数据:无数据


    现在,提交的数据已在 POST 和 FILES 字典之间拆分。该文件可以通过request.FILES['file'] 访问,但不能通过表单清理数据访问,因为该表单因缺少名称而失效。由于我上传的文件很小,它存储在内存中;超过一定大小(默认为 2.5MB)的文件将存储在临时目录中,但您的代码可以处理它们。

    最后,让我们试试这两个字段的值:


    请求详情

    请求方式:POST
    POST 数据:
    文件数据:]}>

    表格详情

    清理的数据:{'name': u'Blair', 'file': }


    由于数据是有效的并且绑定到了表单上,所以也可以通过表单的cleaned_data来访问文件。

    通过request.FILES 访问它有一个潜在的好处:如果表单无效,您仍然可以在要求用户更正数据之前将文件保存在某处。这样可以避免再次上传文件(如果您正在处理大文件,这在时间和带宽方面可能会非常昂贵)。如果您只想处理小文件,那不会有太大区别,但使用request.FILES 可能是更好的做法。这也是Django file upload documentation 的做法。

    【讨论】:

    • 哇,这是很棒的信息!非常感谢您为此付出的努力——如果我能给你一百张赞成票的话。
    • 没问题。我之前并没有真正考虑过这一点——我只是按照之前文档示例的格式进行操作——所以看看它是如何工作的很有趣。
    • @Blair 你应该得到更多的选票。这是一个非常好的解释。谢谢你的回答?
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 2020-11-17
    • 2016-10-05
    相关资源
    最近更新 更多