Django Drama :(多部分表单中的拖放文件上传器)+(将文件绑定到表单/模型并保存到Postgres)

时间:2013-05-30 09:11:22

标签: django file-upload jquery-file-upload django-formwizard

这里埋藏着一些微小的相关问题,但他们确实指出了一个大的,多毛的最佳实践问题。这是一个很难实现的功能,因为它应该一次做几件棘手的事情......

  • 拖放多文件上传器(通过Javascript)
  • 多页表单(第一页:使用现有文档模型上传和关联文件; 第二页:更新并将文件/文档对象和元数据保存到数据库中)

...我还没有在任何地方找到预先存在的代码示例或实现。 (根据一个人的方法,它可以扫除桌面或自动回答所有相关/嵌入/后续问题。)最后,这篇文章的目的是回答这个问题: 什么是最优雅的方法,最大限度地减少干预问题/问题?

我在Django中使用拖放式JQuery File Uploader的这种实现来上传文件......

https://github.com/miki725/Django-jQuery-File-Uploader-Integration-demo

我上面链接的解决方案保存文件系统上的文件,当然,每个上传会话中批量,通过为每批文件创建一个目录,然后为每个文件分配一个UUID那些目录。文件系统上的每个唯一命名目录包含在该特定上载会话期间上载的文件。这意味着任何类型的数据库存储方法首先必须分离和迭代此解决方案为每个上载会话创建的文件系统目录中的所有文件。

注意:上面链接的JQuery解决方案不使用app目录中的表单(在forms.py中)。表单被硬编码到模板中,这已经有点令人失望......因为现在我还必须找到一种很好的方法将每个批处理中的每个上述文件绑定到表单。

认为最简单的 - 尽管可能性能最差的解决方案 - 是为两个表单创建两个视图...将每个文件保存到第一页视图中的数据库,然后在第二页上更新数据库。这是我目前正在推行的方向:

在模板中......

...uploader javascripts in header...

<form action="{% url my_upload_handler %}" method="POST" enctype="multipart/form-data">
    <input type="file" name="files[]" multiple
</form>

在VIEWS.PY ......

def my_upload_handler_0r_form_part_one(request):

#POST(在上传处理程序中;由上传操作触发的请求)

    if request.method == 'POST':
        if not ("f" in request.GET.keys()):

          ...validators and exception handling...
          ...response_data, which is a dict...

            uid = request.POST[u"uid"]

            file = request.FILES[u'files[]']
            filename = os.path.join(temp_path, str(uuid.uuid4()) + file.name)
            destination = open(filename, "wb+")
            for chunk in file.chunks():
                destination.write(chunk)
            destination.close()

            response_data = simplejson.dumps([response_data])
            response_type = "application/json"
            # return the data to the JQuery uploader plugin...
            return HttpResponse(response_data, mimetype=response_type)

#GET(在同一个上传处理程序中)

    else:    
            return render_to_response('my_first_page_template.html',
                              {                     <---NO 'form':form HERE
                              'uid': uuid.uuid4(),
                              },
                              context_instance = RequestContext(request))


def form_part_two(request):

    #here I need to retrieve and update stuff uploaded on first page

    return render_to_response('my_second_page_template.html', 
    {}, 
    context_instance = RequestContext(request))

第一页的这个视图利用了JQuery上传器,它适用于每个会话的多文件上传,并完成它应该做的事情。但是,如上所述,作为上传处理程序的视图只是需要两页表单的第一页。在第二页,最终用户随后需要检索每个上传的文件,将其他数据附加到他们刚刚在第一页上传的文件,然后重新保存到数据库。

我试图通过各种解决方案将这项工作分为两部分,包括表单向导和/或基于通用类的视图......以下示例主要通过会话启用数据持久性。这些解决方案很快就变得非常棘手。

总之,我需要......

  • 以唯一标识的批次(通过拖放)上传多个文件
  • 分开并迭代每批上传的文件
  • 将批处理中的每个文件绑定到表单并将其与现有文档模型相关联
  • 一次性将所有这些文件提交/保存到数据库
  • 检索可能新表单的以下页面/模板上的每个文件
  • 更新每个文件的元数据
  • 一次性将所有这些文件重新提交/保存到数据库

所以...你可以看到上述所有内容如何复杂化简单文件上传的复杂性,并通过涉及相关问题来增加提供功能的复杂性:

forms.py :如何最好地将每个文件绑定到表单

models.py :如何将每个文件与预先存在的文档模型相关联

views.py 如何在第一页的Postgres中按照预先存在的文档模型保存每个文件;更新并保存第二页中的每个文档

...而且,我还想在没有表单向导的情况下执行所有,并且没有基于类的视图。 (CBV,特别是,对于这个用例,我不得不这样做。)换句话说:我正在寻找建议,以实现最安全,最容易阅读/理解的解决方案。如果它导致数据库的多次点击,那对我来说没问题。 (如果将文件保存到数据库似乎是反最佳做法,请参阅另一篇文章:Storing file content in DB

我可以为两个表单创建一个单独的视图,并为标准上传表单创建子类,如此......

在forms.py ...

class FileUploadForm(forms.Form):

    files = forms.FileField(widget=forms.ClearableFileInput(attrs={'name':'files[]', 'multiple':'multiple'}))
    #how to iterate over files in list or batch of files here...?
        file = forms.FileField()

    file = forms.FileField()

    def clean_file(self):
        data = self.cleaned_data["file"]

        # read, parse, and create `data_dict` from file...
        # subclass pre-existing UploadModelForm      
        **form = UploadModelForm(data_dict)**

        if form.is_valid():
            self.instance = form.save(commit=False) 
        else:
            raise forms.ValidationError
        return data

...然后使用类似......

之类的东西重构上面的早期上传处理程序

在views.py中,将以下内容替换为当前上传处理程序...

    def view_for_form_one(request):  
        ...
        # the aforementioned upload handler logic, plus...
        ...
        form = FileUploadForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
        else:
            # display errors
            pass
        ...

    def view_for_form_two(request):

        # update and commit all data here

...

1 个答案:

答案 0 :(得分:2)

通常,对于这类问题,我喜欢在其上创建一个<form>的单个页面,但是用户使用javascript进行多个部分。

使用javascript将表单分成多部分向导式表单系列会更容易,特别是如果它生成的数据本质上是动态的。

如果您绝对必须将其分成多个页面,我建议您设置应用程序,以便在每个步骤结束时将数据保存到数据库中。

您可以通过将用户在步骤2添加的元数据设置为可空字段,甚至将元数据移动到单独的模型来实现。