在 Django 表单中包含 Dropzone 表单时无法进行完整的 POST

Posted

技术标签:

【中文标题】在 Django 表单中包含 Dropzone 表单时无法进行完整的 POST【英文标题】:Cannot make complete POST when including Dropzone form in a Django form 【发布时间】:2016-11-20 09:46:53 【问题描述】:

这绝对是我有史以来最致命的 5 个问题! 我在 Django 中有一个运行良好的 ModelForm。问题是我想在这个表单中添加一个额外的字段——用于上传图片的小 Dropzone.js 区域。使用给定的代码示例,dropzone 预览 DIV 正确嵌入到主窗体中。为了实现这一点,我当然以编程方式初始化了 Dropzone。

如果我评论myDropzone.processQueue();,提交按钮会将常规表单提交到视图,但没有使用 Dropzone 上传的图像。但是,如果processQueue() 被执行,它会覆盖主表单提交操作并且只提交图像。其余内容当然忽略了。

我只想在其余输入字段中提交图像,并且我想在我的 Django 表单中嵌入 Dropzone,因为如果我将整个表单设为 Dropzone 并在其中添加输入字段...整个表单显示为拖放区和样式也乱了。

为了使它更复杂,我有额外的图像数据库表,但我的视图准备额外处理来自请求的图像并将它们处理到数据库。 接下来,我不得不以一种丑陋的方式在 Dropzone 中手动提供 CSRF(使用params: ..),因为 Dropzone 无法识别表单模板引擎中的那个(因为它显然是不同的形式):(

这是模板:

<h1 class="asdf-page-title">
    Add Type
</h1>
<form id="gift-form" class="dropzone-form" method="POST" enctype="multipart/form-data">
    % csrf_token %


     g_form.title 

    <p class="asdf-form-title">Select Type:</p>
    <div class="asdf-form-pic-select row collapse-outer-space ">
        % for choice in g_form.asdf_type %
            <div class="col-4">
                <label class="type- choice.choice_label ">
                     choice.tag 
                    <span>% trans choice.choice_label %</span>
                </label>
            </div>
        % endfor %
    </div>

    <p class="asdf-form-title">Price:</p>
    <div class="asdf-type row collapse-outer-space ">
        <div class="col-4">
             g_form.total_price 
        </div>
        <div class="col-8">
             g_form.currency 
        </div>
    </div>

    <p class="asdf-form-title">Description:</p>
     g_form.description 

    <div class="dropzone dropzone-previews" id="my-awesome-dropzone"></div>

    <p>
        <input id="submit-btn" type="submit" value=" action_btn "
    </p>

</form>

<link href="% static 'admin-users/js/ckeditor/samples/css/samples.css' %" rel="stylesheet" type="text/css"/>
<link href="% static 'admin-users/css/dropzone.css' %" type="text/css" rel="stylesheet"/>
<script src="% static 'admin-users/js/dropzone.js' %"></script>
<script src="% static 'admin-users/js/jquery-2.2.4.min.js' %" type="text/javascript"></script>
<script src="% static 'admin-users/js/ckeditor/ckeditor.js' %" type="text/javascript"></script>
<script>
    Dropzone.autoDiscover = false;
    jQuery(document).ready(function() 

            var myyDropzone = new Dropzone("div#my-awesome-dropzone", 
                    url: "#",
                    params: 'csrfmiddlewaretoken': getCookie('csrftoken'),
                    autoProcessQueue: false,
                    addRemoveLinks: true,
                    maxFilesize: 256 * 4 * 2,
                    maxFiles: 3,
                    uploadMultiple: true,
                    parallelUploads: 10,

                    init: function() 
                        var myDropzone = this,
                            addButton = document.querySelector("#submit");

                        // First change the button to actually tell Dropzone to process the queue.
                        addButton.addEventListener("click", function(e) 
                            // Make sure that the form isn't actually being sent.
                            e.preventDefault();
                            e.stopPropagation();
                            myDropzone.processQueue();
                        );

                        // Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
                        // of the sending event because uploadMultiple is set to true.
                        this.on("sendingmultiple", function() 
                            // Gets triggered when the form is actually being sent.
                            // Hide the success button or the complete form.
                        );
                        this.on("successmultiple", function(files, response) 
                            // Gets triggered when the files have successfully been sent.
                            // Redirect user or notify of success.
                        );
                        this.on("errormultiple", function(files, response) 
                            // Gets triggered when there was an error sending the files.
                            // Maybe show form again, and notify user of error
                        );
                    ,
                    sending: function (file, xhr, formData) 
                        // Along with the file, shall I append all fields from the form above in the formData?
                    
            );
    );
    function getCookie(name) 
        var cookieValue = null;
        if (document.cookie && document.cookie != '') 
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) 
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) 
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                
            
        
        return cookieValue;
    
</script>

【问题讨论】:

您的问题解决了吗? 是的,问题已解决,请在下面查看我的答案。已经快一年了,所以我尝试粘贴和调整工作代码。如果您需要帮助或进一步解释,您可以试一试吗? 【参考方案1】:

我们通过向服务器发出两个请求来解决这个问题:一个 Post 用于常规 Django 表单及其所有数据,一个 Dropzone Ajax 用于上传的图像(如果有)。

这是通过将上传的images添加到Dropzonesending函数中的formdata来实现的。

如果有上传的图片,会调用myDropzone.processQueue(),否则我们会在JavaScript中手动提交表单$(".dropzone-form").submit()

如果myDropzone.processQueue()被执行,那么我们钩入Dropzone事件successmultiple并在一秒后执行$(".dropzone-form").submit()。我们为什么要这样做?好吧,Dropzone 会等到所有图片都上传完毕,然后它会立即触发successmultiple。如果我们直接在那里提交表单,用户将错过 Dropzone 的精彩动画,该动画显示所有上传的图像上都有绿色的勾号。动画持续大约一秒钟,因此延迟。目前我还没有发现比设置超时一秒更好的方法。

代码如下:

Dropzone.autoDiscover = false;

if ($("#my-awesome-dropzone").length > 0)
    var myyDropzone = new Dropzone("#my-awesome-dropzone", 
        url: "some_url",
        autoProcessQueue: false,
        method: "post",
        addRemoveLinks: true,
        maxFilesize: 256 * 4 * 10,
        maxFiles: 3,
        uploadMultiple: true,
        parallelUploads: 10,

        init: function () 
            var myDropzone = this;
            var addButton = $("#submit-btn");

            // First change the button to actually tell Dropzone to process the queue.
            addButton.addEventListener("click", function (e) 
                // Make sure that the form isn't actually being sent.
                e.preventDefault();
                e.stopPropagation();
                if (myDropzone.getQueuedFiles().length > 0) 
                    myDropzone.processQueue();
                 else 
                    $(".dropzone-form").submit();
                
            );

            this.on("successmultiple", function (files, response) 
                setTimeout(function () 
                    $(".dropzone-form").submit();
                , 1000);
            );
        ,
        sending: function (file, xhr, formData) 
            formData.append('csrfmiddlewaretoken', getCookie('csrftoken'));
            formData.append("image", file.name);
        
    );

【讨论】:

非常感谢!我刚刚更改了 var addButton = $("#submit-btn"); by addButton = document.querySelector("#submit"); 谢谢,将我的字段添加到 formData 变量中:formData.append("description", $('#id_description').val()); 如果我们向服务器发出两个请求,我们将如何跟踪哪个记录与哪个图像相关联? 以上乌斯曼先生的观点,我很想看看对此的看法。

以上是关于在 Django 表单中包含 Dropzone 表单时无法进行完整的 POST的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 表单中包含 id 字段?

如何在 Javascript 生成的 HTML 表单中包含 Django 1.2 的 CSRF 令牌?

当我将 dropzone 添加到 Django 中的现有表单时,CSRF 令牌问题

如何在表单的显示中包含不可编辑的信息? [复制]

如何将代码模块中包含的VBA代码放在表单模块中

如何在 django 中包含 urlpatterns?