Django基础五之Ajax
一. Ajax简介
1.简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
a.同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
b.异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程
2.Ajax的应用场景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
a.整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
b.当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
3.Ajax优点:
1.AJAX使用JavaScript技术向服务器发送异步请求;
2.AJAX请求无须刷新整个页面;
3.因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
4. 一个例子
后端代码:
from django.shortcuts import render, HttpResponse from django.http import JsonResponse # Create your views here. from app01 import models def index(request): article_list = models.Article.objects.all() return render(request, 'index.html', {'data_list': article_list}) def del_book(request, pk): if request.method == 'POST': data = {'state': True, 'msg': None} article_id = request.POST.get('article_id') article_obj = models.Article.objects.filter(pk=article_id) if not article_obj.first(): data['state'] = False data['msg'] = '删除对象不存在!' return JsonResponse(data) article_obj.delete() data['msg'] = '删除对象成功!' return JsonResponse(data)
前端:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form method="post"> {% csrf_token %} <table class="table table-striped"> <thead> <tr> <th>序号</th> <th>标题</th> <th>出版社</th> <th>作者</th> <th>编辑</th> </tr> </thead> <tbody> {% for item in data_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ item.title }}</td> <td>{{ item.publish.title }}</td> <td>{% for obj in item.users.all %} {% if forloop.last %} {{ obj.username }} {% else %} {{ obj.username }}, {% endif %} {% endfor %} </td> <td><a class="btn btn-default del_btn" pk="{{ item.pk }}">删除</a></td> </tr> {% endfor %} </tbody> </table> </form> </div> </div> </div> <script src="{% static 'jquery-3.4.1.min.js' %}"></script> <script src="{% static 'jquery.cookie.js' %}"></script> <script> $('.del_btn').on("click", function (event) { var pk = $(this).attr('pk'); var obj = $(this).closest('tr'); $.ajax({ url: `/del_book/${pk}/`, type: "post", headers:{ "X-CSRFToken": $.cookie('csrftoken'), }, data: { {#csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),#} {#csrfmiddlewaretoken: '{{ csrf_token }}',#} article_id: pk, }, success: function (data) { if(data.state){ // 删除成功 obj.remove(); }else{ console.log(data.msg); } } }) }); </script> </body> </html>
注意:
在这里补充个事情:
settings配置文件里面加上下面这句话,意思是说,告诉django,如果别人请求我的路径的时候,你不要自己处理别人输入的路径最后面的/了,如果这个值为True,而我们假如写了一个url为url('^index/',views.test),如果用户输入的时127.0.0.1:8000/index的话,django会让浏览器重新再发一次请求,并且在这个路径后面加上/,也就成了127.0.0.1:8000/index/,此时和我们的url就能匹配上了,因为我们的url正则写的就加了/,如果你将下面这个值设置成false,那么django就不会自动帮你做这个事情了,那么用户在输入127.0.0.1:8000/index,没有最后那个斜杠的路径时,就无法和我们的url正则匹配上了,所以就找不到url了,就会报错,但是注意,django只能帮你重定向让浏览器再发一个get请求,如果你是post请求(非get请求),django就没有办法了,他还是帮你重新定向发送get请求,不能满足你的需求,所以如果你用post方法提交数据的时候,就像上面这个ajax里面的那个url写的必须和你后端配置的那个url对应好,所以别忘了如果你后端url上url('^index/',views.test)这个index后面加了/,那么你写ajax往这个路径下提交数据的时候,ajax里面的url参数后面别忘了写上/,让这个url和后端url正则规则对应好。
二. Ajax使用
1.基于Jquery实现:
<button class="send_Ajax">send_Ajax</button> <script> $(".send_Ajax").click(function(){ $.ajax({ url:"/handle_Ajax/", type:"POST", data:{username:"chao",password:123}, success:function(data){ console.log(data) }, error: function (jqXHR, textStatus, err) { console.log(arguments); }, complete: function (jqXHR, textStatus) { console.log(textStatus); }, statusCode: { '403': function (jqXHR, textStatus, err) { console.log(arguments); }, '400': function (jqXHR, textStatus, err) { console.log(arguments); } } }) }) </script>
2.基于原生js实现
var b2 = document.getElementById("b2"); b2.onclick = function () { // 原生JS var xmlHttp = new XMLHttpRequest(); xmlHttp.open("POST", "/ajax_test/", true); xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlHttp.send("username=chao&password=123456"); xmlHttp.onreadystatechange = function () { if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { alert(xmlHttp.responseText); } }; };
3.Ajax-服务器-Ajax流程图
4.ajax参数
######################------------data---------################ data: 当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格式 (urlencoded:?a=1&b=2)发送给服务端;此外,ajax默认以get方式发送请求。 function testData() { $.ajax("/test",{ //此时的data是一个json形式的对象 data:{ a:1, b:2 } }); //?a=1&b=2 ######################------------processData---------################ processData:声明当前的data数据是否进行转码或预处理,默认为true,即预处理;if为false, 那么对data:{a:1,b:2}会调用json对象的toString()方法,即{a:1,b:2}.toString() ,最后得到一个[object,Object]形式的结果。 ######################------------contentType---------################ contentType:默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。 用来指明当前请求的数据编码格式;urlencoded:?a=1&b=2;如果想以其他方式提交数据, 比如contentType:"application/json",即向服务器发送一个json字符串: $.ajax("/ajax_get",{ data:JSON.stringify({ a:22, b:33 }), contentType:"application/json", type:"POST", }); //{a: 22, b: 33} 注意:contentType:"application/json"一旦设定,data必须是json字符串,不能是json对象 views.py: json.loads(request.body.decode("utf8")) ######################------------traditional---------################ traditional:一般是我们的data数据有数组时会用到 :data:{a:22,b:33,c:["x","y"]}, traditional为false会对数据进行深层次迭代;
三 Ajax请求设置csrf_token
方式一:
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "chao",
"password": 123456,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
},
success: function (data) {
console.log(data);
}
})
方式2:
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
方式3
注意:需要jquery,cookie.js。地址:https://plugins.jquery.com/cookie/
<script src="{% static 'js/jquery.cookie.js' %}"></script>
$.ajax({ headers:{"X-CSRFToken":$.cookie('csrftoken')}, #其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加 })
详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
所以解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。
那么django中csrf认证怎么玩的呢?
我又有疑问了,同一次登录,form表单中的token每次都会变,而cookie中的token不便,django把那个salt存储在哪里才能保证验证通过呢。直到看到源码。
token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。
django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。
注意:
如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。
如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。
这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。
django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def login(request):
pass
四. Ajax文件上传
请求头ContentType
ContentType指的是请求体的编码类型,常见的类型共有3种:
1 application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form> 表单,如果不设置 enctype
属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。请求类似于下面这样(无关的请求头在本文中都省略掉了):
POST http://www.example.com HTTP/1.1 Content-Type: application/x-www-form-urlencoded;charset=utf-8 user=yuan&age=22 #这就是上面这种contenttype规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,都是这样拼接数据,大家公认的一种数据格式,但是如果你contenttype指定
的是urlencoded类型,但是post请求体里面的数据是下面那种json的格式,那么就出错了,服务端没法解开数据。
2 multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype
等于 multipart/form-data,form表单不支持发json类型的contenttype格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。直接来看一个请求示例:
<form> 用户名 <input type="text" id="user"> 头像 <input type="file" id="avatar"> <input type="button" id="ajax-submit" value="ajax-submit"> </form> <script> $("#ajax-submit").click(function(){ var formdata=new FormData(); formdata.append("user",$("#user").val()); formdata.append("avatar_img",$("#avatar")[0].files[0]); $.ajax({ url:"", type:"post", data:formdata, processData: false , // 不处理数据 contentType: false, // 不设置内容类型 success:function(data){ console.log(data) } }) }) </script>
3 application/json
application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
如果在ajax里面写上这个contenttype类型,那么data参数对应的数据,就不能是个object类型数据了,必须是json字符串,contenttype:'json',简写一个json,它也能识别是application/json类型
服务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据,其实就是服务端框架已经写好了针对这几个类型的不同的解析数据的方法,通过contenttype值来找对应方法解析,如果有一天你写了一个contenttype类型,定义了一个消息格式,各大语言及框架都支持,那么别人也会写一个针对你的contenttype值来解析数据的方法,django里面不能帮我们解析contenttype值为json的数据格式,你知道他能帮你解析application/x-www-form-urlencoded 和multipart/form-data(文件上传会用到)就行了,如果我们传json类型的话,需要我们自己来写一个解析数据的方法,其实不管是什么类型,我们都可以通过原始发送来的数据来进行加工处理,解析出自己想要的数据。
$.ajax({
url:"{% url 'home' %}",
type:'post',
headers:{
"X-CSRFToken":$.cookie('csrftoken'),
contentType:'json',
},
data:JSON.stringify({ //如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,必须通过csrf的方式3的形式来写,写在hearders
(请求头,可以写一些自定制的请求头)里面,注意,其实contentType也是headers里面的一部分,写在里面外面都可以
name:name,
//csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
}),
success:function (response) {
}
})
4. 基于form表单的文件上传
<form action="" method="post" enctype="multipart/form-data"> #上面说的其他两种contenttype都是键值的形式发送数据,这种form_data的格式一般是把大数据一段一段隔开的 用户名 <input type="text" name="user"> 头像 <input type="file" name="avatar"> #如果不用form_data格式来发,那么默认的是urlencoded的格式,这个标签的数据会组成avatar:文件名字来进行发送 <input type="submit"> </form>
视图部分:
def index(request): print(request.body) # 原始的请求体数据 print(request.GET) # GET请求数据 print(request.POST) # POST请求数据 print(request.FILES) # 上传的文件数据 return render(request,"index.html")
def upload(request): if request.method == 'GET': return render(request,'upload.html') else: print(request.POST) username = request.POST.get('user') file_obj = request.FILES.get('file_obj') #获得文件数据对象 print('>>>',file_obj,type(file_obj)) #>>> jaden博客.txt <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>,一个文件对象,可以理解为一个文件句柄 file_name = file_obj.name #jaden博客.txt print(file_name) # 将数据写到文件里面,需要名字,需要数据 with open(file_name,'wb') as f: #直接把文件名字放这里,那么文件将直接生成在django的整个项目目录下,因为django配置的系统搜索的根路径就是咱们的项目文件夹路径,
# 那个BASE_DIR,一般我们需要自己建立一个文件夹专门存放上传的文件
#所以需要我们自己来拼接一个路径放到这里,os.path.join(settings.BASE_DIR,'media','img',file_name)
# f.write() #不能一下写进去,占用的内容太多,要一点一点写 for data in file_obj: #读数据 f.write(data) #每次读取的data不是固定长度的,和读取其他文件一样,每次读一行,识别符为\r \n \r\n,遇到这几个符号就算是读了一行
#for chunks in file_obj.chunks(): #chunks()默认一次返回大小为经测试为65536B,也就是64KB,最大为2.5M,是一个生成器
# f.write(chunks)
注意:
1.写入数据的时候,最好用 for chunks in file_obj.chunks():来写入,避免大量数一下涌入内存。
2.通过js来找文件对象
5.基于Ajax的文件上传
模板:
<form id='form1'> #用不用form没关系,这里就是个盒子的作用,一般写form标签是为了提示别人,这个地方的内容是要提交的 {% csrf_token %} 用户名 <input type="text" id="user"> 头像 <input type="file" id="avatar"> <input type="button" id="ajax-submit" value="ajax-submit"> </form> <script> $("#ajax-submit").click(function(){ var formdata=new FormData(); #ajax上传文件的时候,需要这个类型,它会将添加给它的键值对加工成formdata的类型 formdata.append("user",$("#user").val()); #添加键值的方法是append,注意写法,键和值之间是逗号 formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); #别忘了csrf_token formdata.append("avatar_img",$("#avatar")[0].files[0]); $.ajax({ url:"", type:"post", data:formdata, #将添加好数据的formdata放到data这里 processData: false , // 不处理数据 contentType: false, // 不设置内容类型 success:function(data){ console.log(data) } }) }) </script>
注意:用formdata添加键值对的时候,别忘记了csrf_token!
也可以这样写:
var form = document.getElementById("form1");
var fd = new FormData(form);
这样也可以直接通过ajax 的 send() 方法将 fd 发送到后台。
注意:由于 FormData 是 XMLHttpRequest Level 2 新增的接口,现在 低于IE10 的IE浏览器不支持 FormData。
五. 关于json
1.什么是json
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言 *
- JSON 具有自我描述性,更易理解
* JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
json数据类型和python数据类型的对比:
object和python的dict类型是差不多的,但是要求里面必须是双引号,string和list、tuple等也是一样的,都是双引号。python中的datetime等时间日期类型是不能进行json序列化的,因为json没有对应的格式,上面的这几种数据类型虽然进行json.dumps序列化之后都是个字符串,但是也是有格式的
前端ajax拿到后端返回的一个python的json模块序列化之后的一个json字符串,那么js通过自己的json接口,将接受到的json字符串来反序列化为js自己语言能够识别的数据类型,然后再进行操作。
相当于我有一个json方法,你有一个json方法,你给我发数据必须是json字符串的格式,那么你就需要将你的数据类型序列化为json的字符串,那么序列化的时候,就把你的数据序列化为了符合json标准的字符串,然后我接收到这个字符串之后,我通过我的json方法,将数据转换为我的语言支持的数据类型。在进行反序列化的时候,如果你的字符串不符合json的格式,那么反序列化的时候就会报错,所以只要你是通过json序列化成的字符串,都是能够json反序列化的,因为json序列化的时候,就把你的数据改为了符合json标准的字符串形式,例如:里面的单引号,序列化后变成了双引号。
合格的json对象:
["one", "two", "three"] { "one": 1, "two": 2, "three": 3 } #这就是一个json的object类型,符合json的标准格式,就可以通过dumps来进行序列化 {"names": ["张三", "李四"] } [ { "name": "张三"}, {"name": "李四"} ]
不合格的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;} // 不能使用函数和日期对象
}
看一下普通字符串和json字符串,在进行序列化的时候的区别
import json # s = "{'name':'chao','age':18}" #普通字符串,每加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。 s = '{"name":"chao","age":18}' #json字符串,里面必须是双引号 ret = json.loads(s) print(ret) print(ret['name'])
2.js的stringify与parse方法
JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象
JSON.parse('{"name":"chao"}'); JSON.parse('{name:"chao"}') ; // 错误 JSON.parse('[18,undefined]') ; // 错误
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。
JSON.stringify({"name":"chao"})
3. Django内置的serializers做序列化
看代码:
from django.core import serializers
def books_json(request): book_list = models.Book.objects.all()[0:10] from django.core import serializers ret = serializers.serialize("json", book_list) return HttpResponse(ret)
通过json学列化时间日期格式数据的时候需要注意,不能直接序列化,可以写一个类来:
import json from datetime import datetime from datetime import date #对含有日期格式数据的json数据进行转换 class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field,date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder) print(dd)
六. 补充一个SweetAlert插件示例
$(".btn-danger").on("click", function () {
swal({
title: "你确定要删除吗?",
text: "删除可就找不回来了哦!",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn-danger",
confirmButtonText: "删除",
cancelButtonText: "取消",
closeOnConfirm: false
},
function () {
var deleteId = $(this).parent().parent().attr("data_id");
$.ajax({
url: "/delete_book/",
type: "post",
data: {"id": deleteId},
success: function (data) {
if (data.status === 1) {
swal("删除成功!", "你可以准备跑路了!", "success");
} else {
swal("删除失败", "你可以再尝试一下!", "error")
}
}
})
});
})
项目中应用:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'bootstrap-sweetalert-master/dist/sweetalert.css' %}"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form method="post"> {% csrf_token %} <table class="table table-striped"> <thead> <tr> <th>序号</th> <th>标题</th> <th>出版社</th> <th>作者</th> <th>编辑</th> </tr> </thead> <tbody> {% for item in data_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ item.title }}</td> <td>{{ item.publish.title }}</td> <td>{% for obj in item.users.all %} {% if forloop.last %} {{ obj.username }} {% else %} {{ obj.username }}, {% endif %} {% endfor %} </td> <td><a class="btn btn-default del_btn" pk="{{ item.pk }}">删除</a></td> </tr> {% endfor %} </tbody> </table> </form> </div> </div> </div> <script src="{% static 'jquery-3.4.1.min.js' %}"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> <script src="{% static 'bootstrap-sweetalert-master/dist/sweetalert.js' %}"></script> <script src="{% static 'jquery.cookie.js' %}"></script> <script> $('.del_btn').on("click", function (event) { var pk = $(this).attr('pk'); var obj = $(this).closest('tr'); swal({ title: "你确定要删除吗?", text: "删除可就找不回来了哦!", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "我已经下定决心", cancelButtonText: "容我三思", closeOnConfirm: false }, function () { $.ajax({ url: `/del_book/${pk}/`, type: "post", data: {"article_id": pk, csrfmiddlewaretoken: '{{ csrf_token }}'}, success: function (data) { console.log(data,typeof data); if (data.state) { swal("删除成功!", "你可以准备跑路了!", "success"); setTimeout(function () { obj.remove(); },1000) } else { swal("删除失败", data.msg, "error") } } }) }); }); </script> </body> </html>
views.py:
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
# Create your views here.
from app01 import models
def index(request):
article_list = models.Article.objects.all()
return render(request, 'index.html', {'data_list': article_list})
def del_book(request, pk):
if request.method == 'POST':
print(f'pk是{pk}')
data = {'state': True, 'msg': None}
article_id = request.POST.get('article_id')
article_obj = models.Article.objects.filter(pk=article_id)
if not article_obj.first():
data['state'] = False
data['msg'] = '删除对象不存在!'
return JsonResponse(data)
article_obj.delete()
data['msg'] = '删除对象成功!'
return JsonResponse(data)
urls:
from django.contrib import admin from django.urls import path, re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), re_path(r'^del_book/(?P<pk>\d+)/$', views.del_book, name='del_book') ]