django ajax 可能用到的技术细节我们来做个总结。
软件环境
软件 | 版本 |
---|---|
python | 3.6.6 |
django | 2.0 |
jquery | 1.8.3 |
技术注意点:
- 小样板 Example
- csrf 解决方案
- python 序列化对象
- js 端如何使用对象
- 补不足
小样板Example
urls.py
urlpatterns = [
....,
url(r'^author/save/$', book_views.author_save)
]
views.py
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def author_save(request):
if request.method == "POST":
authorName = request.POST.get('authorName', None)
authorAge = request.POST.get('authorAge', 20)
print("author %s %d" % (authorName, int(authorAge)))
# return HttpResponse( json.dump({'new_author':new_author}) )
author = Author()
author.name = authorName
author.age = int(authorAge)
author.save()
return HttpResponse( json.dumps( author, default=author2dict ) )
author_admin.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>作者管理</title>
{% load static %}
<script src="{% static 'js/jquery-1.8.3.min.js' %}"></script>
<link href="{% static 'css/bookstyle.css' %}?v=2" type="text/css" rel="stylesheet"/>
</head>
<style>
#formDiv {
margin-top:30px;
margin-left:10px;
overflow: hidden;
}
#formDiv *{
float: left;
}
#formDiv > div{
clear: both;
}
</style>
<body>
<div id="show">
<ul>
<li>
<div>作者</div>
<div>年龄</div>
</li>
</ul>
</div>
<div id="formDiv">
{% csrf_token %}
<div><input type="text" name="authorName"/></div>
<div><input type="text" name="authorAge"/></div>
<div><button id="c2">提交</button></div>
</div>
<script type="text/javascript">
//jquery 方法
$(document).ready(function(){
$('#c2').on('click', function(){
var authorName = $('input[name=authorName]').val();
var authorAge = $('input[name=authorAge]').val();
console.log( "authorName: " + authorName);
var data = {
authorName: authorName,
authorAge: authorAge
};
$.ajax({
url: '/author/save/',
type: 'post',
data: data,
async:true,
csrfmiddlewaretoken:'{{ csrf_token }}',
success: function (res) {
add2list(JSON.parse(res));
alert('作者添加成功')
},
error:function () {
}
});
})
var add2list = function(data){
console.log(data.name)
$('#show > ul').append('<li><div></div><div></div></li>')
$('#show > ul > li:last > div:eq(0)').html(data.name)
$('#show > ul > li:last > div:eq(1)').html(data.age)
};
});
</script>
</body>
</html>
csrf 解决方案
CSRF(Cross-site request forgery跨站请求伪造),不同的语言框架, 解决方案比较多, 这是一种服务器端的, 访问安全协议。 这种危害性是比较大的, 网站的请求可能会被随意的使用和篡改。
一般服务器端默认会限制跨域的访问请求。django通过中间件来默认限制访问.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
注意: ‘django.middleware.csrf.CsrfViewMiddleware’,
这个访问,默认会限制AJAX的请求, 也是比较麻烦, 所以
– 方案1 就是 去掉这个组件,
– 方案2 使用装饰器
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def .....
– 方案3 (1,2)方案本质上都是关闭了csrf的限制, 如果一个请求是比较重要的,可以使用csrf_token 来实现csrf的客户端到服务器端的,验证请求。
<div id="formDiv">
{% csrf_token %}
</div>
<script>
$.ajax({
url: '/author/save/',
type: 'post',
data: data,
async:true,
csrfmiddlewaretoken:'{{ csrf_token }}',
success: function (res) {
add2list(JSON.parse(res));
alert('作者添加成功')
},
error:function () {
}
});
// 或者使用一下代码
$(function(){
$.ajaxSetup({
beforeSend: function(xhr,settings){
xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
}
});
$('#btn1').click(function () {
$.ajax({
url: '/login/',
type:"GET",
data: {'user': 'root', 'pwd': '123'},
// headers: {'X-CSRFtoken': $.cookie('csrftoken')},
success:function(arg){
}
})
});
})
</script>
注意:
{% csrf_token %}
是通过settings.py 里的
SECRET_KEY = ‘tcyehgzal46yvsb_++zt&c-m+ozkm327a3xlwb+p%n2gp-v%*8’
加了一把salt 一起生成的心的token
会生成一个 这个值是服务器端的token 用来在前端生成后 交给服务器端验证
ajax提交的时候 csrfmiddlewaretoken:’{{ csrf_token }}’, 来提交数据
遗憾的是这个解决方案, 我在本地测试没通过, 可能是字符打错了, 这个大家可以继续尝试这个问题解决了, 可以吧 {{ csrf_token }} 字段写在data属性里由ajax一起提交
python 序列化
python 默认可以使用 json.dumps(..) 来初始化 dict 对象, 但是对普通的object 对象序列化会直接报一个, [Python]json 错误xx is not JSON serializable 错误。 这个问题的详细叙述可以看一下廖雪峰关于序列化的详细解释 我就不重述了。
Anyway,从httpResponse传对象给 客户端的代码是
def author2dict(author):
return { 'name': author.name,
'age': author.age
}
def author_save(request):
if request.method == "POST":
authorName = request.POST.get('authorName', None)
authorAge = request.POST.get('authorAge', 20)
print("author %s %d" % (authorName, int(authorAge)))
# return HttpResponse( json.dump({'new_author':new_author}) )
author = Author()
author.name = authorName
author.age = int(authorAge)
author.save()
return HttpResponse( json.dumps( author, default=author2dict ) )
注意: 这里的Author 对象的定义是
class Author(models.Model):
name=models.CharField(max_length=20)
age=models.IntegerField()
book=models.ManyToManyField(Books) #book_id
这里的 book属性 我在上面
def author2dict(author):
return { ‘name’: author.name,
‘age’: author.age
}
并没有设置, 尝试了集中方式, 这个 如果大家有方案的话可以告诉我。
js 端如何使用对象
从服务端发出的 对象最后是以json格式传输的, 其实客户端拿到的就是一串字符串, 并不是json对象。 所以需要最后使用js 转义一下即可。
<script>
success: function (res) {
add2list(JSON.parse(res));
alert('作者添加成功')
},
</script>
最后就可以使用 res.name 的方式取出对象数据
补充不足
可以在views.py里 返回的时候直接使用JsonResponse 这样放回的就直接是
json格式了, 前端也不需要JSON.parse