Ajax
Posted Rannie
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ajax相关的知识,希望对你有一定的参考价值。
Ajax知识准备Json
什么是Json
JSON 指的是 javascript 对象表示法(JavaScript Object Notation)
JSON 是轻量级的文本数据交换格式
stringify与parse方法
JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象
JSON.parse(\'{"name":"Howker"}\'); JSON.parse(\'{name:"Stack"}\') ; // 错误 JSON.parse(\'[18,undefined]\') ; // 错误
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串
JSON.stringify({"name":"Tonny"})
Ajax简介
Ajax,全称为Asynchronous JavaScript and XML,即异步的JavaScript和XML。
它不是一门编程语言,
而是利用JavaScript在保证页面不被刷新、页面链接不改变 的 情况下与服务器交换数据 并更新部分网页的技术
同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;阻塞
Ajax异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。非阻塞,通过异步回调机制 callback()返回结果
与后端进行交互的方式:
浏览器地址栏输入url: GET
a标签href:GET/POST
form表单:GET/POST
Ajax: GET/POST
初始Ajax
案例:页面有三个input框,一个按钮
用户在前两个框中输入数字,点击按钮保证页面不刷新的情况下将数据发到后端做计算
将计算好的结果再发给前端展示到第三个input框中,基于jQuery封装的Ajax
index.html
<input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3"> <button id="d1">按钮</button> <script> $(\'#d1\').click(function () { // 获取两个框里的内容,向后端提交异步请求 // ajax基本语法 $.ajax({ // 1 向哪个后盾提交数据 url: \'\', // 2 指定提交当前请求的方式 type: \'post\', // 3 提交的数据 data: {\'i1\':$(\'#i1\').val(), \'i2\':$(\'#i2\').val()}, // 4 ajax是异步提交,所以需要给一个回调函数来处理返回结果 success: function (data) { // data就是异步提交的返回结果 // 将异步回调的结果通过DOM操作渲染到第三个input框中 $(\'#i3\').val(data) } }) }) </script>
views.py
def index(request): if request.method == \'POST\': i1 = request.POST.get(\'i1\') i2 = request.POST.get(\'i2\') i3 = int(i1) + int(i2) return HttpResponse(i3) return render(request,\'index.html\')
urls.py
from django.conf.urls import url from app01 import views urlpatterns = [ url(r\'^index/\', views.index) ]
Ajax常见应用常见
搜索引擎根据用户输入关键字,自动提示检索关键字
还有一个很重要的应用场景就是注册时候的用户名的查重
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来
整个过程中页面没有刷新,只是局部刷新了;
在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作
Ajax优缺点
- AJAX使用JavaScript技术向服务器发送异步请求;
- AJAX请求无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
- 两个关键点:1.局部刷新,2.异步请求
$.ajax()方法参数
url:
要求为String类型的参数,(默认为当前页地址)发送请求的地址。
type:
要求为String类型的参数,请求方式(post或get)默认为get。注意其他http请求方法,例如put和delete也可以使用,但仅部分浏览器支持。
data:
要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格式。get请求中将附加在url后。防止这种自动转换,可以查看
processData
选项。对象必须为key/value格式,例如{foo1:"bar1",foo2:"bar2"}转换为&foo1=bar1&foo2=bar2。
如果是数组,JQuery将自动为不同值对应同一个名称。例如{foo:["bar1","bar2"]}转换为&foo=bar1&foo=bar2。
success:
要求为Function类型的参数,请求成功后调用的回调函数
由服务器返回,并根据dataType参数进行处理后的数据。
success:function (data){ //data可能是xmlDoc、jsonObj、html、text等等 }
Content-Type
content-type前后端传输数据的编码格式
urlencoded
formdata
application/json
form表单
默认是urlencoded编码格式传输数据
urlencoded数据格式
username=santa&password=123
django后端针对该格式的数据,会自动解析并帮你打包到request.POST中
formdata数据格式
- django后端针对符合urlencoded编码格式数据(普通键值对)还是统一解析到request.POST中
- 而针对formdata文件数据就会自动解析放到request.FILES中
ajax提交
ajax默认的也是urlencoded编码格式
前后端数据交互 编码格式与数据格式一定要一致
application/json
- django后端针对json格式数据 并不会做任何的处理,而是直接放在request.body中
Ajax发送数据
基于Ajax发送json数据
1 指定编码格式为application/json
2 将数据序列化为json串,保证与编码格式一致(前端的序列化JSON)
3 后端处理json格式的数据,不会做任何处理,原封不动的封装到request.body中
<button id="d3">ajax发送文件数据</button> <script> $(\'#d3\').click(function () { $.ajax({ url: \'\', type: \'post\', // 修改content-type 参数 contentType: \'application/json\', data: JSON.stringify({\'username\': \'santa\', \'password\': \'123\'}), // 将数据序列化成json格式字符串 success: function (data) { alert(data) } }) }) </script>
views.py
import json def ab_ct(request): if request.method == \'POST\': print(request.body) # b\'{"username":"santa","password":"123"}\' json_bytes = request.body # json_str = json_bytes.decode(\'utf-8\') # json_dict = json.loads(json_str) # print(json_dict) # {\'username\': \'santa\', \'password\': \'123\'} # 扩展 json.loads能够自动解码并序列化 json_dict = json.loads(json_bytes) print(json_dict) # {\'username\': \'santa\', \'password\': \'123\'} return render(request, \'ab_ct.html\')
基于Ajax发送文件数据
1 借助于内置对象new FormData()
2 该对象可以携带文件数据,也可以传键值对
3 在Ajax中指定两个参数:
contentType:false 代表不使用任何编码格式,
processData:false 代表不对数据本身做任何处理,FormData()实例化的对象内部自带编码,django后端能够识别
<input type="file" name="myfile" id="i1"> <button id="d3">ajax发送文件数据</button> <script> $(\'#d3\').click(function () { // 1 需要先生成一个内置对象 var myFormData = new FormData(); // 2 传普通键值对,当普通键值对较多的时候,我们可以利用for循环来添加 myFormData.append(\'username\', \'santa\'); myFormData.append(\'password\', 123); // 3 传文件 myFormData.append(\'myfile\', $(\'#i1\')[0].files[0]); // 发送Ajax请求 $.ajax({ url: \'\', type: \'post\', data: myFormData, // 发送formdata对象需要指定两个关键性的参数 processData:false, // 让浏览器不要对你的数据进行任何的操作 contentType:false, // 不要使用任何编码格式 对象formdata自带编码格式并且django能够识别该对象 success: function (data) { alert(data) } }) }) </script>
views.py
def ab_ct(request): if request.method == \'POST\': print(request.FILES) # <MultiValueDict: {\'myfile\': [<InMemoryUploadedFile: DJANGO点击.png (image/png)>]}> return render(request, \'ab_ct.html\')
Django内置序列化功能(了解)
序列化目的:
将数据整合成一个大字典形式,再传给前端,方便数据的交互
一般用于前后端分离开发
drf django restframework from app01 import models from django.core import serializers # 以视图函数index为例 def ab_se(request): user_queryset = models.Userinfo.objects.all() # user_list = [] # for user_obj in user_queryset: # user_list.append({ # \'username\':user_obj.username, # \'password\':user_obj.password, # \'gender\':user_obj.get_gender_display(), # }) # res = json.dumps(user_list) res = serializers.serialize(\'json\',user_queryset) # return render(request,\'ab_se.html\',locals()) return HttpResponse(res) # res是一个字典套字典{{},{},{},{}...} # 每一个数据表中的记录作为一个字典 # 前端接收到的只有所有的字典,没有最外层的字典
批量插入数据
bulk_create
def ab_bc(request): # 先插入1000条件数据 # for i in range(1,1001): # models.Book.objects.create(title=\'第%s本书\'%i) book_list = [] for i in range(1,10001): book_list.append(models.Book(title=\'新的%s书\'%i)) models.Book.objects.bulk_create(book_list) # 批量插入数据的方式 book_queryset = models.Book.objects.all() return render(request,\'ab_bc.html\',locals())
自定义分页器
分页数据是通过总数据切片而来,queryset对象支持正数索引和正向步长,不支持负数
html
{% for book_obj in page_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} {{ page_obj.page_html|safe }}
views.py
from app01 import models from app01.utils.mypage import Pagination def ab_bc(request): book_queryset = models.Book.objects.all() current_page = request.GET.get(\'page\', 1) all_count = book_queryset.count() # 1 现生成一个自定义分页器类对象 page_obj = Pagination(current_page=current_page,all_count=all_count,pager_count=9) # 2 针对真实的queryset数据进行切片操作 page_queryset = book_queryset[page_obj.start:page_obj.end] return render(request,\'ab_bc.html\',locals())
mypage.py
class Pagination(object): def __init__(self, current_page, all_count, per_page_num=10, pager_count=11): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 用法: queryset = model.objects.all() page_obj = Pagination(current_page,all_count) page_data = queryset[page_obj.start:page_obj.end] 获取数据用page_data而不再使用原始的queryset 获取前端分页样式用page_obj.page_html """ 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>\' <ul class=\'pagination\'> \'\'\') first_page = \'<li><a href="?page=%s">首页</a></li>\' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = \'<li class="disabled"><a href="#">上一页</a></li>\' else: prev_page = \'<li><a 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"><a href="?page=%s">%s</a></li>\' % (i, i,) else: temp = \'<li><a 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"><a href="#">下一页</a></li>\' else: next_page = \'<li><a href="?page=%s">下一页</a></li>\' % (self.current_page + 1,) page_html_list.append(next_page) last_page = \'<li><a 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)
Ajax结合sweetalert实现删除二次确认
前端book_list.html
{% extends \'home.html\' %} {% block content %} <br> <p><a href="{% url \'add_books\' %}" class="btn-danger btn">新增</a></p> <br> <br> <table class="table-striped table-hover table"> <thead> <tr> <th>序号</th> <th>书名</th> <th>价格</th> <th>出版时间</th> <th>出版社</th> <th>作者</th> <th>操作</th> </tr> </thead> <tbody> {% for book_obj in page_queryset %} <tr> <td>{{ forloop.counter }}</td> <td>{{ book_obj.title }}</td> <td>{{ book_obj.price }}</td> <td>{{ book_obj.publish_time|date:\'Y-m-d\' }}</td> <td>{{ book_obj.publish.name }}</td> <td> {% for author_obj in book_obj.authors.all %} {% if forloop.last %} {{ author_obj.name }} {% else %} {{ author_obj.name }}, {% endif %} {% endfor %} </td> <td> <a href="{% url \'edit_bookss\' book_obj.pk %}" class="btn btn-primary">编辑</a> <a href="{% url \'del_book\' book_obj.pk%}" class="btn btn-primary" >删除</a> <a href="#" class="btn btn-primary can" adel_id="{{ book_obj.pk }}">测试删除</a> </td> </tr> {% endfor %} </tbody> </table> {{ page_obj.page_html|safe }} <script> $(\'.can\').click(function () { var $aEle =$(this); swal({ title: "确定删除?", text: "You will not be able to recover this imaginary file!", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "Yes, delete it!", cancelButtonText: "No, cancel plx!", closeOnConfirm: false, closeOnCancel: false }, function(isConfirm) { if (isConfirm) { $.ajax({ url: \'/adel_book/\', type: \'post\', data: {\'adel_id\': $aEle.attr("adel_id")}, success: function (data) { if (data.code === 1000){ swal("删除成功!", "Your imaginary file has been deleted.", "success"); $aEle.parent().parent().remove() } else { swal("删除失败", "Your imaginary file is safe :)", "error"); } } }); } else { swal("怂笔", "你成功的刷新我对你的认知", "error"); } }); }) </script> {% endblock %}
views.py
def adelete_book(request): if request.method == \'POST\': back_dic = {"code": 1000, "msg": \'\'} delete_id = request.POST.get(\'adel_id\') models.Book.objects.filter(pk=delete_id).delete() back_dic[\'msg\'] = \'删除成功,跑路\' return JsonResponse(back_dic) return redirect(reverse(\'all_book\'))
注意点:
1 在标签内自定义一个属性
adel_id="{{ book_obj.pk }}
,绑定事件click2 通过 $(this) 得到当前标签
3 如果要删除,就会回传当前数据的
adel_id
给服务器,通过 $(this).attr(\'adel_id\')获取要删除的数据id, 在Ajax的data中定义键值对,4 通过DOM操作,实现局部的数据交互更新,但整个页面并不刷新
django中如何开启事务
Django文件传输
以上是关于Ajax的主要内容,如果未能解决你的问题,请参考以下文章