在做Django项目时,用到了很多AJax的知识,说到Ajax就会涉及到json的知识,因此索性准备来一篇博客,将项目过程中遇到的问题记录下,以方便日后的查阅。
一.什么是JSon?
JSON(javascript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。
它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
说到这里大家可能还是难以理解,说白了,json就是一种通用的数据交换格式,有了json,我们就可以在不同语言之间交互数据,能够非常方便我们的操作,我们来看这样一个需求:Python负责爬取数据,Java用来做大数据分析:
例如:Python负责爬取数据,这些数据构成了一个大的字典,现在需要给Java处理!如果直接发送给Java处理,Java肯定处理不了,那是因为你Python中的字典,我Java压根不认识,因为你们的语法不一样,所以我处理不了!但是现在我就要处理这些数据,现在就需要一套标准,这套标准就是用来定义Python和Java之间的数据交换协议——例如这里的Json,我将Python的字典序列化成json格式的字符串,然后你Java拿到后再使用json进行反序列化成你认识的对象,这样我们就完成了不同语言之间的数据交换
再例如,我们使用Python做Web开发,经常需要和前端JS交互数据,此时又会用到json,例如将Python生成的字典序列化成如下json的字符串传递给前端,前端在使用json反序列化成JS能够识别的字符串即可。
大家要记住:
从上图中可以看到,Python中的基本数据类型和json中的基本数据类型存在一系列对应关系,例如Python中的字典对象对应到json中object类型,也就是说但凡是支持json数据格式的语言,其基本数据类型都和json有对应关系,有了这层对应关系,就可以直接使用json进行序列化,这也就是为什么我们在之前的Django项目中使用可以使用JSonresponse直接序列化一个字典对象的原因,并且前端可以不用反序列化就可以使用的原因,因为Python中的字典和json中object对象类型对应,所以不需要使用json.dumps先序列化成字符串,然后再返回给前端。
笔者曾经遇到过一个大坑:日期类型的序列化
我们每次对文章评论完毕后,都会显示评论时间,这个评论时间就是最终的入库时间,既然要在前端进行展示,我们需要将时间取出来然后传递回去,但问题在于我们在定义数据模型的时候,时间是一个DateTimeField的字段类型,JSon是无法序列化一个datetime.datetime的日期对象的,因为没有一个对应的关系,也就是说我们不能直接使用JsonResponse来进行序列化,要不然会报错:
前面我们说过,既然无法直接序列化,我就使用python中的json.dumps来先将你序列化成字符串,然后再传递给前端即可,这也就是我们为什么在comment视图中使用HttpResponse来传递后端数据,而不使用JsonResponse来序列化数据的原因,来看代码即可:
def comment(request):
"""
服务端需要客户端传递过来三个数据:
1.评论内容,2.评论的文章,3.当前评论的根评论的ID:parent_comment_id
由于只有登入的用户可以评论,所以当前的用户ID就是登入用户的ID
如果当前评论存在根评论,那说明这次提交的是子评论,否则提交的就是根评论
"""
comment_content = request.POST.get("content")
article_id = request.POST.get("article_id")
parent_comment_id = request.POST.get("parent_comment_id")
user_id = request.user.nid
"""
注意最终渲染到页面上的时间,是该条评论的入库时间,而不是该条评论的提交时间;
"""
comment_response = {}
if parent_comment_id: # 提交的是子评论,构建评论树
"""
如果提交的是子评论,那么同样需要往comment表中插入一条记录,同时要
往文章表中的评论数+1,这里又是一个事务操作,这里使用create方法往评论表中插入一条数据的时候会得到一个
评论对象current_comment
"""
with transaction.atomic():
current_comment = Comment.objects.create(content=comment_content, user_id=user_id, article_id=article_id,
parent_comment_id=parent_comment_id)
Article.objects.filter(article_id=article_id).update(comment_count=F("comment_count") + 1)
comment_response["parent_comment_content"] = current_comment.parent_comment.content
comment_response["parent_comment_user"] = current_comment.parent_comment.user.username
else: # 提交的就是根评论
"""
将根评论内容添加至comment表中
然后将文章表中的评论数comment_count自加1
"""
with transaction.atomic():
current_comment = Comment.objects.create(content=comment_content, user_id=user_id, article_id=article_id)
Article.objects.filter(article_id=article_id).update(comment_count=F("comment_count") + 1)
# 给前端ajax返回相应的数据进行渲染
"""
这里有2点需要注意:
1.如果直接通过current_comment.comment_date获取当前记录的入库时间,此时该时间是一个datetime对象
如果直接使用进行序列化会报错。
"""
comment_response["content"] = current_comment.content
comment_response["comment_date"] = current_comment.comment_date.strftime("%Y-%m-%d %H:%m")
comment_response["comment_id"] = current_comment.nid
return HttpResponse(json.dumps(comment_response))
在上面的思路中,我们自己处理,借助于datetime模块的strfttime完成对日期对象的处理,然后使用json.dumps来序列化最终的字符串对象,如下所示:
comment_response["comment_date"] = current_comment.comment_date.strftime("%Y-%m-%d %H:%m")
print(type(comment_response["comment_date"])) 此时它是一个普通的Python字符串对象
笔者继续给大家举一个实际例子:我们之前使用Ajax将编辑后的老师信息发送给后端数据库进行修改,看如下的代码:
$(document).ready(function () {
//给table表里面的所有modal编辑按钮绑定事件,使用Jquery的事件委派
$("table").on("click", ".m-edit", function () {
//一旦点击modal编辑讲师按钮,即可弹出模态框,myEditModal为模态框的唯一ID
$("#myEditModal").modal("show");
//此时模态框中已经有值存在了,这是通过Ajax发出的get请求获取的值,要不然我们是无法重新编辑值的,接下来我们取值
var $tds = $(this).parent().parent().children(); // 获取表格中所有的td标签
var teacherID = $($tds[0]).text();
var teacherName = $($tds[1]).text();
var teacherGender = $($tds[2]).text();
var teacherSalary = $($tds[3]).text();
//使用Ajax获取老师任教的班级,因为teacherID和teacherName在请求teacher_list时,就可以获取到
//但是老师的班级是个列表怎么获取,我们这里使用Ajax来获取
$.ajax({
url: "/modal_teacher_list/?teacher_id="+teacherID,
type: "GET",
success: function (data) {
console.log(data);
var classIDs = JSON.parse(data);
// 获取当前老师所带班级的ID
$("#editTeacherSelect").val(classIDs);
}
});
//然后来填充模态框中各个字段的值
$("#editTeacherName").val(teacherName);
$("#editTeacherGender").val(teacherGender);
$("#editTeacherSalary").val(teacherSalary);
$("#editTeacherID").val(teacherID);
});
// 给编辑老师信息模态框的提交按钮绑定事件
$("#edit-modal-submit").on("click", function () {
//获取编辑之后的值
var teacherID = $("#editTeacherID").val();
var teacherName = $("#editTeacherName").val();
var teacherGender = $("#editTeacherGender").val();
var teacherSalary = $("#editTeacherSalary").val();
// 获取编辑后老师所带的班级ID,一个老师带多个班级,所以这里得到的是一个数组,思考后面怎么将数组传递到后台
var classIDs = $("#editTeacherSelect").val();
//使用Ajax提交到后端
$.ajax({
url: "/modal_edit_teacher/",
type: "post",
//注意使用Ajax提交数组到前端时,需要使用JSON.stringify()序列化为字符串
data: {"teacher_id": teacherID, "teacher_name":teacherName, "teacher_gender": teacherGender,
"teacher_salary": teacherSalary, "class_ids": JSON.stringify(classIDs)},
success: function (data) {
//从前端接接收到的为json字符串,需要反序列化为js对象
var dataObj = JSON.parse(data);
if (dataObj.status === 0){
location.reload()
}else{
//如果更新失败,那就隐藏模态框,同时弹出swall效果
$("#myEditModal").modal("hide");
swal("我擦", "更新失败了", "error");
}
}
})
})
});
在上面的需求中,一个老师可以带领多个班级,这里的班级在JS中是一个数组,我们不可能直接将数组放在Ajax中的data中进行传递,因此需要使用JS的序列化方法,将其序列为一个字符串对象,然后再进行传递,如下:
data: {"teacher_id": teacherID, "teacher_name":teacherName, "teacher_gender": teacherGender,"teacher_salary": teacherSalary, "class_ids": JSON.stringify(classIDs)}
后台在收到序列化后的班级列表字符串后,肯定需要使用Python来反序列化为Python中的列表对象:
teacher_class_ids = request.POST.get("class_ids")
teacher_ids = json.loads(teacher_class_ids)
二.正确认识JSon格式
笔者在做项目的过程中经常将JSON对象,JSON字符串以及两者之间互转弄混淆,基于此,笔者在此整理一下,方便日后查阅:
【001】Json对象:是指符合json格式要求的js对象,前面笔者提到过json就是来源于JS;既然是对象,那就可以使用对象名.属性名来调用,看如下的例子:
var person={"name":"zhangsan","sex":"男","age":"24"}; //json对象
alert(person.name);//zhangsan
alert(typeof person);//object
person就是json对象。可以用perosn.name这种方式进行属性的调用。第三行代码就是看person的类型,为object类型;注意json对象中的属性名必须要是双引号,例如下面这样的格式是不对的:
{ name: "张三", ‘age‘: 32 } // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用undefined
{ "name": "张三",
"birthday": new Date(‘Fri, 26 Aug 2011 07:13:10 GMT‘),
"getName": function() {return this.name;} // 不能使用函数和日期对象
}
【002】字符串,我们常说的JavaScript中的字符串是单引号或者双引号引起来的,而Json字符串指的是符合json格式要求的js字符串,如下:
var person=‘{"name":"zhangsan","sex":"男","age":"24"}‘;//json字符串
alert(person);//{"name":"zhangsan","sex":"男","age":"24"}
alert(typeof person);//string
person就是一个json字符串,之所以叫json字符串,因为字符串的格式符合json的格式,第三行代码也匹配其中的类型为string。
【003】JSON字符串和JOSN对象的转换
json字符串转json对象,调用parse方法:
var person=‘{"name":"zhangsan","sex":"男","age":"24"}‘;//json字符串
var personObject = JSON.parse(person);
alert(personObject.name);//zhangsan
json对象转为json字符串,调用stringify方法:
var person={"name":"zhangsan","sex":"男","age":"24"};//json对象
var personString = JSON.stringify(person);
alert(personString);
关于Django中Ajax的使用我们将在下节中详细说明,希望各位好好掌握本篇博客的知识!
【004】stringify与parse方法
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象
eg:
console.log(JSON.parse(‘{"name":"Yuan"}‘));
console.log(JSON.parse(‘{name:"Yuan"}‘)) ; // 错误
console.log(JSON.parse(‘[12,undefined]‘)) ; // 错误
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。
eg: console.log(JSON.stringify({‘name‘:"egon"})) ;
【005】 最后给各位推荐一个可以格式化JSon字符串的在线网址,非常方便:https://www.bejson.com/
参考网址:https://www.cnblogs.com/gyx19930120/p/4419971.html
http://blog.csdn.net/android_xue/article/details/69488793