在 Django 表单中包含 Dropzone 表单时无法完成 POST
Cannot make complete POST when including Dropzone form in a Django form
这绝对是我有史以来最杀人的 5 大问题!
我在 Django 中有一个 ModelForm,它工作正常。问题是我想在此表单中添加一个额外的字段 - 用于图像上传的小 Dropzone.js 区域。使用给定的代码示例,dropzone 预览 DIV 已正确嵌入到主窗体中。为了实现这一点,我当然以编程方式初始化了 Dropzone。
如果我评论 myDropzone.processQueue();
提交按钮将常规表单提交到视图,但没有使用 Dropzone 上传的图像。但是,如果 processQueue()
被执行,它会覆盖主表单提交操作并且只提交图像。其余内容当然忽略
我只想在其余输入字段中提交图像,我想将 Dropzone 嵌入到我的 Django 表单中,因为如果我将整个表单设为 Dropzone 并在其中添加输入字段...整个表单是显示为拖放区域,样式也乱七八糟。
为了让它更复杂,我有额外的数据库 table 用于图像,但我的视图准备额外处理来自请求的图像并将它们处理到数据库。
接下来,我不得不以一种丑陋的方式(使用 params: {..}
)在 Dropzone 中手动提供 CSRF,因为 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>
我们通过向服务器发出两个请求解决了这个问题:一个 Post 用于常规 Django 表单及其所有数据,一个 Dropzone Ajax 用于上传的图像(如果有)。
这是通过在Dropzone
的sending
函数中将上传的images
添加到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);
}
});
}
这绝对是我有史以来最杀人的 5 大问题! 我在 Django 中有一个 ModelForm,它工作正常。问题是我想在此表单中添加一个额外的字段 - 用于图像上传的小 Dropzone.js 区域。使用给定的代码示例,dropzone 预览 DIV 已正确嵌入到主窗体中。为了实现这一点,我当然以编程方式初始化了 Dropzone。
如果我评论 myDropzone.processQueue();
提交按钮将常规表单提交到视图,但没有使用 Dropzone 上传的图像。但是,如果 processQueue()
被执行,它会覆盖主表单提交操作并且只提交图像。其余内容当然忽略
我只想在其余输入字段中提交图像,我想将 Dropzone 嵌入到我的 Django 表单中,因为如果我将整个表单设为 Dropzone 并在其中添加输入字段...整个表单是显示为拖放区域,样式也乱七八糟。
为了让它更复杂,我有额外的数据库 table 用于图像,但我的视图准备额外处理来自请求的图像并将它们处理到数据库。
接下来,我不得不以一种丑陋的方式(使用 params: {..}
)在 Dropzone 中手动提供 CSRF,因为 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>
我们通过向服务器发出两个请求解决了这个问题:一个 Post 用于常规 Django 表单及其所有数据,一个 Dropzone Ajax 用于上传的图像(如果有)。
这是通过在Dropzone
的sending
函数中将上传的images
添加到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);
}
});
}