django之--Ajax
Ajax准备知识:JSON
什么是 JSON ?
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言 *
- JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
stringify与parse方法
JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象(json只认双引的字符串格式)
JSON.parse('{"name":"Howker"}');
JSON.parse('{name:"Stack"}') ; // 错误
JSON.parse('[18,undefined]') ; // 错误
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。
JSON.stringify({"name":"Tonny"})
Json和XML的比较
JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁琐笨重的 XML 格式。
JSON 格式有两个显著的优点:书写简单,一目了然;符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。
XML和JSON都使用结构化方法来标记数据,下面来做一个简单的比较。
用XML表示中国部分省市数据如下:
<?xml version="1.0" encoding="utf-8"?> <country> <name>中国</name> <province> <name>黑龙江</name> <cities> <city>哈尔滨</city> <city>大庆</city> </cities> </province> <province> <name>广东</name> <cities> <city>广州</city> <city>深圳</city> <city>珠海</city> </cities> </province> <province> <name>台湾</name> <cities> <city>台北</city> <city>高雄</city> </cities> </province> <province> <name>新疆</name> <cities> <city>乌鲁木齐</city> </cities> </province> </country>
用JSON表示如下:
{ "name": "中国", "province": [{ "name": "黑龙江", "cities": { "city": ["哈尔滨", "大庆"] } }, { "name": "广东", "cities": { "city": ["广州", "深圳", "珠海"] } }, { "name": "台湾", "cities": { "city": ["台北", "高雄"] } }, { "name": "新疆", "cities": { "city": ["乌鲁木齐"] } }] }
由上面的两端代码可以看出,JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽。
Ajax介绍
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
""" 异步提交 局部刷新 例子:github注册 动态获取用户名实时的跟后端确认并实时展示的前端(局部刷新) 朝发送请求的方式 1.浏览器地址栏直接输入url回车 GET请求 2.a标签href属性 GET请求 3.form表单 GET请求/POST请求 4.ajax GET请求/POST请求 AJAX 不是新的编程语言,而是一种使用现有标准的新方法(比较装饰器) AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程) Ajax我们只学习jQuery封装之后的版本(不学原生的 原生的复杂并且在实际项目中也一般不用) 所以我们在前端页面使用ajax的时候需要确保导入了jQuery
ps:并不只有jQuery能够实现ajax,其他的框架也可以 但是换汤不换药 原理是一样的 """
示例:
页面输入两个整数,通过AJAX传输到后端计算出结果并返回。(整个过程页面不准有刷新,也不能在前端计算)<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3">
<button id="b1">Ajax Test</button>
<script src="/static/jquery-3.3.1.min.js"></script>
<script>
$('#b1').click(function () {
$.ajax({
url:'', // 1.指定向哪个后端提交ajax请求,不填则默认向当前地址提交
type:'POST', // 2.指定请求提交方式:get/post 默认是get请求
data:{i1:$('#i1').val(),i2:$('#i2').val()}, // 3.要提交给后端的数据
//dataType:'json', //预期服务器返回的数据类型
success:function (data) { // 4.回调函数:当后端返回结果时会自动触发,args用来接收后端的返回结果
$('#i3').val(data)
}
})
})
</script>
</body>
</html>def ajax_test(request):
if request.method=='POST':
i1=request.POST.get('i1')
i2=request.POST.get('i2')
ret=int(i1)+int(i2)
return HttpResponse(ret)
return render(request,'ajax_test.html')
"""
针对后端返回字典类型数据:
HttpResponse返回 (注意:HttpResponse只能返回字符串)
必须先将字典转成json格式才能返回,否则无法返回完整的字典
JsonResponse返回,底层已经自动的帮你序列化成了json数据返回
针对前端接收字典类型数据:
dataType //预期服务器返回的数据类型
HttpResponse返回的json数据,回调函数不会自动帮我们反序列化
需要我们自己手动来反序列化
方法1:在ajax中设置dataType参数
方法2:自己手动JSON.parse()来反序列化
JsonResponse返回的json数据,回调函数会自动进行反序列化,拿到手的就是一个对象了
"""
views.py
from django.conf.urls import url from app01 import views urlpatterns=[ url(r'^ajax_test/',views.ajax_test), ]
AJAX的优缺点
优点:
- AJAX使用JavaScript技术向服务器发送异步请求;
- AJAX请求无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
- 两个关键点:1.局部刷新,2.异步请求
前后端传输数据编码格式
'''前提:前后端数据传输时一定要保证编码格式与数据真正格式是一致的''' # get请求数据的编码格式直接跟在了url后面 '''url?name=123&age=123''' ''' 可以发送post请求的方式: 1、form表单 2、ajax请求 前后端传输数据的编码格式: 1、urlencoded 2、formdata 3、json ''' # form表单post提交: ''' 1、默认提交的编码格式:urlencoded 数据格式:user=yumi&pwd=123 django后端针对符合urlencoded编码格式的数据会自动解析封装到request.POST中 2、涉及文件提交的编码格式:form-data 数据格式:不显示 那么针对普通数据还是解析封装到request.POST中,但是文件数据解析封装到了request.FILES中了 3、form表单无法提交json格式数据 ''' # ajax请求post提交: ''' 1、默认提交的编码格式:urlencoded 数据格式:user=yumi&pwd=123&file=C%3A%5Cfakepath%5C2020-03-21-19-28-39_0.png django后端针对符合urlencoded编码格式的数据会自动解析封装到request.POST中 '''
ajax发送json格式数据
''' <script> $('#btn').click(function(){ $.alax({ url:'', type:'post', data:JSON.stringify({'user': 'yumi', 'age': 21}), contentType:'applecation/json', success:function(args){} }) }) </script> ''' # 提交方式:applecation/json # 数据格式:{"user":"yumi","age":21} json_byte = request.body # json_str = json_byte.decode(json_byte) # json.loads括号内如果传入了一个二进制格式的数据其内部会自动解码再反序列化 dic = json.loads(json_byte) ''' 注意:针对json数据,后端不会做任何处理,也就不会帮你解析封装到request.POST中 而是变成了二进制的请求,此时你可以用request.body来获取二进制请求数据 request对象方法补充:request.is_ajax()判断请求是否是ajax请求,返回布尔值 ''' ''' ajax请求发送json格式数据时需要注意: 1、要指定提交方式:设置contentType参数并且指定为'application/json' 2、数据必须是真正的json格式数据:data:JSON.stringify({'user': 'yumi', 'age': 21}) 3、针对json格式数据django后端需要自己通过request.body来手动获取处理 '''
序列化--Django内置的serializers
什么意思呢?就是我的前段想拿到由ORM得到的数据库里面的一个个用户对象,我的后端想直接将实例化出来的数据对象直接发送给客户端,那么这个时候,就可以用Django给我们提供的序列化方式
def ser(request): #拿到用户表里面的所有的用户对象 user_list=models.User.objects.all() #导入内置序列化模块 from django.core import serializers #调用该模块下的方法,第一个参数是你想以什么样的方式序列化你的数据 ret=serializers.serialize('json',user_list) return HttpResponse(ret)
ajax发送文件
''' $('#btn').click(function () { // 1 需要先利用FormData内置对象 let formData = new FormData(); // 2 添加普通的键值对 formData.append('user', $('#inp1').val()); formData.append('pwd', $('#inp2').val()); // 3 添加文件对象 formData.append('file', $('#f1')[0].files[0]); //先将jQuery对象转成标签对象再进一步获取文件数据 // 4 将对象基于ajax发送给后端 $.ajax({ url:'', type:'post', data:formData, //直接将对象放data后面即可 //ajax发送文件时必须要指定的两个参数: contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象 processData:false, // 告诉浏览器不要对你的数据进行任何处理 success:function (args) { {#alert(args)#} } }) }) ''' if request.is_ajax(): if request.method == 'POST': print(request.POST) print(request.FILES) ''' 总结: 1、利用FormData内置对象 let formData = new FormData() 2、针对普通键值对,直接往对象里添加即可 formData.append('username',$('#d1').val()); 3、针对文件数据,需要先将文件对应的jQuery对象转成标签对象再通过files获取真正的文件数据再添加到formData对象中 4、ajax发送文件时必须要指定两个关键性的参数 contentType:false, // 不需使用任何编码 processData:false, // 告诉浏览器不要对你的数据进行任何处理 5、django后端自动识别formdata对象 自动将内部的普通键值对解析封装到request.POST中 自动将文件数据解析封装到request.FILES中 '''
ajax结合sweetalert实现删除按钮的二次确认操作
''' 知道如何CV代码 学会基于别人代码的基础之上做修改 研究别人代码中各个参数的意思,再照猫画虎的加些效果 ''' ''' $('.del').click(function () { let deleteBtn = $(this); swal({ title: "真的要删除吗?", text: "请确认以备份数据", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "删除", cancelButtonText: "返回", closeOnConfirm: false, closeOnCancel: false, showLoaderOnConfirm: true //遇到ajax请求遇到延迟时的等待效果 }, function (isConfirm) { if (isConfirm) { $.ajax({ // 朝后端发送ajax请求删除数据之后 再弹下面的提示框 url:'', type:'post', data:{'del_id':deleteBtn.attr('delete_id')}, success:function (args) { swal("删除成功", "已删除", "success"); if (args.code === 1000) { // 判断响应状态码 然后做不同的处理 swal("删除成功", args.msg, "success"); // 删除成功后刷新页面的两种方式 // 1、直接刷新当前页面 // window.location.reload() // 2、利用DOM操作,动态刷新 deleteBtn.parent().parent().remove(); } else { swal("删除失败", '内部未知错误', "info"); } } }) } else { swal("删除失败", "取消删除", "error"); } }); '''
django自带的序列化组件(drf铺垫)
# 需求:在前端给我获取到后端用户表里面所有的数据 并且要是列表套字典 '''只需将获取的数据重新组成一个个字典然后添加到列表中转成json格式数据传给前端即可''' # 方法一:手动拼接字典 ''' from django.http import JsonResponse def ab_serializers(request): queryset = models.User.objects.all() tmp_list = [] for obj in queryset: tmp = { 'pk': obj.pk, 'user': obj.user, 'age': obj.age, 'gender': obj.gender } tmp_list.append(tmp) return JsonResponse(tmp_list, safe=False) ''' # 方法二:使用serializers模块 from django.core import serializers ''' from django.core import serializers def ab_ser(request): # 需求:在前端给我获取到后端用户表里面所有的数据 并且要是列表套字典 queryset = models.User.objects.all() """会自动将数据序列化变成json格式的字符串 并且内部非常的全面""" res = serializers.serialize('json', queryset) return HttpResponse(res) ''' """ [ {"pk": 1, "username": "jason", "age": 25, "gender": "male"}, {"pk": 2, "username": "egon", "age": 31, "gender": "female"}, {"pk": 3, "username": "kevin", "age": 32, "gender": "others"}, {"pk": 4, "username": "tank", "age": 40, "gender": 4} ] 前后端分离的项目 作为后端开发的你只需要写代码将数据处理好 能够序列化返回给前端即可 再写一个接口文档 告诉前端每个字段代表的意思即可 [ { "model": "app01.user", "pk": 1, "fields": {"username": "jason", "age": 25, "gender": 1}}, { "model": "app01.user", "pk": 2, "fields": {"username": "egon", "age": 31, "gender": 2}}, { "model": "app01.user", "pk": 3, "fields": {"username": "kevin", "age": 32, "gender": 3}}, { "model": "app01.user", "pk": 4, "fields": {"username": "tank", "age": 40, "gender": 4}} ] 写接口就是利用序列化组件渲染数据然后写一个接口文档 该交代交代一下就完事 """
批量插入
''' 批量插入数据的时候 使用orm给提供的bulk_create能够大大的减少操作时间 ''' def ab_pl(request): # 先给Book插入一万条数据 # for i in range(10000): # models.Book.objects.create(title='第%s本书'%i) # # 再将所有的数据查询并展示到前端页面 book_queryset = models.Book.objects.all() # 批量插入 # book_list = [] # for i in range(100000): # book_obj = models.Book(title='第%s本书'%i) # book_list.append(book_obj) # models.Book.objects.bulk_create(book_list) """ 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间 :param request: :return: """ return render(request,'ab_pl.html',locals())
自己写一个分页器(只需掌握分页器的推到思路即可)
""" 总数据100 每页展示10 需要10 总数据101 每页展示10 需要11 总数据99 每页展示10 需要10 在制作页码个数的时候 一般情况下都是奇数个 符合中国人对称美的标准 """ '''分页器逻辑推导''' # 当前页面 current_page = request.GET.get('page') count_book = models.Book.objects.count() # 每页数据量 per_page_num = 10 # 解压复制获取总页数以及多出的数据 count_page, more = divmod(count_book, per_page_num) if more: # 多处数据存在则总页数+1 count_page += 1 # 将前端传回来的字符串页面数转成整形,页面不存在则设置当前页为第一页 try: current_page = int(current_page) if current_page > count_page: current_page = 1 except Exception: current_page = 1 # 起始位置 start_page = (current_page - 1) * per_page_num # 终止位置 end_page = current_page * per_page_num page_html = '' is_active = current_page if current_page < 6: current_page = 6 for i in range(current_page-5, current_page+6): # 限制页码显示个数为11 if is_active == i: # 在后端写好html代码然后前端通关转义直接使用 page_html += '<li class="page-item active"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i) else: page_html += '<li class="page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i) book_queryset = models.Book.objects.all()[start_page:end_page] return render(request, 'ab_pl.html', locals()) ''' django有自带的分页器模块但是书写起来太过麻烦且功能太简单,因此推荐使用自定义分页器 对于上述分页器代码,只需要知道内部逻辑推导即可, 基于上述思路已经封装好了自定义分页器,之后有需要直接拷贝使用即可 '''
自定义分页器封装代码
自定义封装代码
# 自定义分页器 class Pagination(object): def __init__(self, current_page, all_count, per_page_num=2, pager_count=7): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul标签 page_html_list.append(''' <nav aria-label='Page navigation example'> <ul class='pagination'> ''') first_page = '<li class="page-item"><a class="page-link" href="?page=%s">首页</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled page-item"><a class="page-link" href="#">上一页</a></li>' else: prev_page = '<li class="page-item"><a class="page-link" href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): if i == self.current_page: temp = '<li class="active page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li class="page-item"><a class="page-link" href="?page=%s">%s</a></li>' % (i, i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled page-item"><a class="page-link" href="#">下一页</a></li>' else: next_page = '<li class="page-item"><a class="page-link" href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li class="page-item"><a class="page-link" href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加标签 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list)
自定义分页器----后端使用
def get_book(request): book_list = models.Book.objects.all() current_page = request.GET.get("page",1) all_count = book_list.count() page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10) page_queryset = book_list[page_obj.start:page_obj.end] return render(request,'booklist.html',locals())
自定义分页器----前端使用
<div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {% for book in page_queryset %} <p>{{ book.title }}</p> {% endfor %} {{ page_obj.page_html|safe }} </div> </div> </div>