我正试图通过AJAX上传一个HTML表单(只有JS,没有jQuery)。该表单是由我的模板通过添加三个组件组装而成的:csrf令牌、ModelForm和常规Django表单(forms.form)。模型表单{{form.as_p}}包含表单的可见部分,而表单{{order_form}}包含一些隐藏字段。我的模板的表单部分如下所示:
<form id="{{ form_id }}" action="javascript:submitThisForm('{{ form_id }}', '/submit_blog_entry/')" method='POST' enctype='multipart/form-data'>
{% csrf_token %}
{{ form.as_p }}
{{ other_form }}
<input type='submit' value='SAVE changes' />
</form>
我已经尝试从<form>
标记中删除enctype(我在回复另一个问题时读到FormData()自动添加了这个),但没有成功。
当按下提交按钮时,将调用JS函数submitBlodEntryForm(),传递用于AJAX请求的表单ID和url。JS函数的代码在这里:
function submitThisForm(form_ID, url){
var submit_form = document.getElementById(form_ID);
var formData = new FormData(document.getElementById(form_ID));
httpRequest = new XMLHttpRequest();
if (!httpRequest){
alert("Giving up, cannot create an XMLHTTP instance.");
return false;
};
var url = "/submit_blog_entry/";
var sendCSRFtoken = "csrfmiddlewaretoken="+String(getCookie('csrftoken'));
var sendContent = sendCSRFtoken+"&"+formData;
httpRequest.onreadystatechange = alertContents;
httpRequest.open('POST', url, true);
httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//httpRequest.send();
httpRequest.send(sendContent);
// alternatively: httpRequest.send(formData);
}
AJAX请求被提交到服务器并由服务器接收(Django视图)。如果我没有手动添加上面JS代码中所示的csrf令牌(变量sendContent),只发送formData,我会收到403错误,显然是因为服务器找不到令牌。它应该是形式的一部分,不过。。。
当我尝试将收到的数据绑定到相应的表单时,验证失败:
form = ThisForm(request.POST)
if form.is_valid():
#do something
如果我打印所需内容。POST,我在终端中得到以下内容:
<QueryDict: {'[object FormData]': [''], 'csrfmiddlewaretoken': ['token goes here']}>
显然,FormData对象是空的。我之所以这么认为,是因为我的表单中的两个必填字段(通过使用form.errors.as_data())出现了以下两个错误:
[ValidationError(['This field is required.'])]
出了什么问题?我是否把模板搞砸了,以至于FormData()无法生成有用的数据?我是否错误地创建了AJAX请求?还是服务器端的问题(尽管到目前为止我几乎没有在那里做任何事情)?
谢谢你,非常感谢你的帮助!
最好不要像这样将表单元素传递给FormData:
new FormData(document.getElementById(form_ID))
我几乎可以肯定,它只有firefox支持。其他浏览器不会自动填充对象。
还有你做的:
var sendContent = sendCSRFtoken+"&"+formData;
由于"sendCSRFtoken"是一个字符串,它调用formData上的toString()方法并将两者连接起来,这就是为什么在django端获得"[object formData]"的原因。
使这项工作添加表单字段的一种方法是:
formData.append(name, value);
对CRSF令牌执行同样的操作,然后调用send,如下所示:
httpRequest.send(formData);
XMLHttpRequest有多个用于发送的重载,因此如果您愿意,也可以发送编码字符串:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send()
在调试时,在ajax调用之前在chrome开发工具中打开网络选项卡,以验证发布的内容是否正确,然后再排除客户端,这将非常有帮助。
感谢大家。我现在发现了问题,愚蠢的复制&粘贴创建AJAX请求的JS函数submitBlogEntryForm()不正确。设置httpRequest标头
httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
导致与编码指令相矛盾。我只是完全删除了这一行,也没有在模板的表单标记中指定"enctype",而是让FormData()自动设置所有这些。现在它工作了!
再次感谢您的帮助!
有2个问题
- 您必须使用FormData.append将数据添加到使用FormData的请求中
- FormData对象在请求中使用多部分/表单数据内容类型(该类型是自动正确设置的)
...
var url = "/submit_blog_entry/";
formData.append("csrfmiddlewaretoken",getCookie('csrftoken'));
httpRequest.onreadystatechange = alertContents;
httpRequest.open('POST', url, true);
//httpRequest.send();
httpRequest.send(formData);
...