Django 在文件上传中引发 MultiValueDictKeyError

Posted

技术标签:

【中文标题】Django 在文件上传中引发 MultiValueDictKeyError【英文标题】:Django raises MultiValueDictKeyError in File Upload 【发布时间】:2014-05-14 22:10:45 【问题描述】:

我已经咨询了很多论坛,但我无法得到答案。我已经在我的 Django 应用程序中安装了文件上传,以将数据保存到我的服务器中。但它不起作用。相反,它会引发 MultiValueDictKeyError。我想问题是没有 request.FILES (因为它会在 request.FILES 提及中引发错误),所以文件上传不起作用。这是我的views.py:

def list_files(request, phase_id):
    phase = get_object_or_404(Phase, pk=int(phase_id))  
    if request.method == 'POST':
    #form = DocumentForm(request.POST, request.FILES)
    form = DocumentForm(request.POST, request.FILES)
    if form.is_valid():
        newdoc = Document(docfile = request.FILES['docfile'], phase = phase_id)
        newdoc.save()
        doc_to_save = request.FILES['docfile']
        filename = doc_to_save._get_name()
        fd = open(settings.MEDIA_URL+'documents/'+str(filename),'wb')
        for chunk in doc_to_save.chunks():
            fd.write(chunk)
        fd.close()

        return HttpResponseRedirect(reverse('list_files')) 
    else:
        form = DocumentForm()

    documents = Document.objects.filter(phase=phase_id)

    return render_to_response('teams_test/list_files.html','documents': documents, 'form':form, 'phase':phase, context_instance = RequestContext(request)
    )

forms.py中的文档表单:

class DocumentForm(forms.ModelForm):
    docfile = forms.FileField(label='Select a file', help_text='max. 42 megabytes')
    class Meta:
    model = Document

models.py中的类文档:

class Document(models.Model):
    docfile = models.FileField(upload_to='documents')
    phase = models.ForeignKey(Phase)

最后,我的html代码:

% extends "layouts/app.html" %
% load i18n  user %

% block title %% trans "Files list" %% endblock %
% block robots %noindex,nofollow% endblock %


% block page%

<div id="page" class="container">
    <div class="header prepend-2 span-20 append-2 last whiteboard">
        <h2 style="margin-left:-40px"> phase.name  files</h2>

        % if documents %
        <ul>
        % for document in documents %
        <li><a href=" document.docfile.url "> document.docfile.name 
        % endfor %
        </ul>
    % else %
        <p>No documents.</p>
    % endif %

        <form action="% url list_files phase.id %" method="post" enctype="multipart/form-data">
        % csrf_token %
        <input id="file" type="file" />
        <input id="submit" type="submit" value="Upload file" />
        </form>
  </div> 
</div>
% endblock %

我的回溯说:

Exception Type: MultiValueDictKeyError
Exception Value:    "Key 'docfile' not found in <MultiValueDict: >"
my_dir/views.py in list_files
    newdoc = Document(docfile = request.FILES['docfile'], phase = phase_id) 

而我的 QueryDict 是空的:

POST:<QueryDict: u'csrfmiddlewaretoken': [u'UZSwiLaJ78PqSjwSlh3srGReICzTEWY1']>

为什么?我究竟做错了什么?

提前致谢。

【问题讨论】:

【参考方案1】:

您需要将multipart/form_data 更改为multipart/form-data - 这就是request.FILES 为空的原因:由于拼写错误,表单没有按照Django 期望的方式发送内容。 [编辑:现在已经完成]

更新 1:另外,不要直接访问 request.FILES,而是尝试依赖模型表单的默认行为,因为这样它就会被适当地作为上传处理。即,newdoc = form.save() 应该可以满足您的所有需求,快速浏览一下 - 当 modelform 可以为您执行此操作时,您手动保存文件是否有特殊原因?

更新 2:啊,看:您没有为文件上传元素分配名称

来自文档:

HttpRequest.FILES 一个类似字典的对象,包含所有上传的文件。 FILES 中的每个键都是来自&lt;input type="file" name="" /&gt; 的名称。 FILES 中的每个值都是一个 UploadedFile

所以,你需要改变

<input id="file" type="file" />

或者,对于默认的 Django 约定

<input id="id_docfile" type="file" name="docfile"/>

确实,使用 Django 表单来呈现实际字段通常会更好,即使您已经超越了整个 form.as_p 方法:

form.docfile

PS。如果您还没有完全阅读它们,我衷心建议您花时间阅读所有forms documentation

【讨论】:

在您更改为form-data后,已更新,并提出了另一个建议 没有特别的原因,我只是在测试不同的选项来解决问题。但是,如果问题出在request.FILES中,应该不会是文件传输出现任何错误吧? html中的表格什么的。我不知道,但是任何数据都会到达我的 views.py。 我已经更改了我的代码,包括form.save(),但没有任何更改。但是这里有一些奇怪的东西:错误是由评论抛出的。在我的回溯中:my_dir/views.py in list_files#newdoc = Document(docfile = request.FILES['docfile'], phase = phase_id) .../home/lince/teams/env/lib/python2.6/site-packages/django/utils/datastructures.py in __getitem__raise MultiValueDictKeyError("Key %r not found in %r" % (key, self)) ...这怎么可能? 我的错,最后这件事解决了(评论的错误)。当我这样做时:if form.is_valid() 它会抛出 False。问题的根源在哪里? 再次更新 - 与您form.is_valid 查询无关,但也应该解决这个问题。【参考方案2】:

修改 Post 方法为

<form action="" method="post" enctype="multipart/form-data">% csrf_token %

【讨论】:

您为什么希望这会有所帮助(您要改变什么?只是将行动指向自我而不是 OP 指向的内容?修复 enctype 是公认答案建议的第一件事) 这就是我需要添加的全部内容。【参考方案3】:

对于那些尝试了上述方法但仍然无法找到解决方案的人。这是我所做的:

views.py

if request.method == 'POST':
    doc = request.FILES #returns a dict-like object
    doc_name = doc['filename']
    ...

【讨论】:

【参考方案4】:

对于尝试了上述方法但仍然无法找到解决方案的任何人。这是我所做的(第二部分):

if request.method == 'POST' and 'filename' in request.FILES:
    doc = request.FILES #returns a dict-like object
    doc_name = doc['filename']
    ...

【讨论】:

以上是关于Django 在文件上传中引发 MultiValueDictKeyError的主要内容,如果未能解决你的问题,请参考以下文章

Django 文件上传大小限制

django 在 / 'file' 处引发 MultiValueDictKeyError

Django python View and Form - MultiValueDictKeyError at /result 'num1' - 为啥我的表单不会提交数据

Django ImageField 保存到位置

在邮递员中上传文件有效,但在 axios 中会引发 500 错误

如何在 Django 中更改上传文件的文件名?