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,其他的框架也可以 但是换汤不换药 原理是一样的我们学的是jQuery版本的ajax,必须要确保html页面已经提前加载了jQuery
"""
Ajax准备知识:JSON
什么是json?
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言 *
- JSON 具有自我描述性,更易理解
- JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。
啥都别多说了,上图吧!
合格的json对象(json只认双引的字符串格式):
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]
stringify与parse方法
JavaScript中关于JSON对象和字符串转换的两个方法:
JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象(json只认双引的字符串格式)
JSON.parse('{"name":"Howker"}');
JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。
JSON.stringify({"name":"Tonny"})
Ajax简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
基本语法:
$.ajax({
url:'', # 朝后端哪个地址发送 跟action三种书写方式一致
type:'get/post', # 提交方式 默认get 跟form表单method参数一致
data:{'username':'jason','password':123}, # 要发送的数据
success:function(args){
# 异步回调处理机制
}
})
"""
当你在利用ajax进行前后端交互的时候
后端无论返回什么都只会被回调函数接受 而不再影响这个浏览器页面了
"""
# 扩展 参数 代码发布项目还会涉及
dataType:'json'
"""
当后端是以HttpResponse返回的json格式的数据
默认是不会自动反序列化的
1.自己手动JSON.parse()
2.配置dataType参数
"""
补充写法
<script>
$("#id_submit").click(function () {
// 朝后端发送ajax请求
$.ajax({
// 1、指定朝哪个后端发ajax请求
url:"", // 不写就是朝当前地址提交
// 2、请求方式
type:"post", //不指定默认就是get,都是小写,用ajax提交post数据,后端也是在request.POST中接收
dataType:"json",
data:{
"username":$("#name").val(),
"password":$("#password").val()
},
// 4、回调函数:当后端给你返回结果的时候会自动触发 args接收后端的返回结果
success:function (args) {
console.log(args) // 通过DOM操作动态渲染到第三个input里面
}
})
.done(function(resp) { // 请求成功以后的操作
console.log(resp);
})
.fail(function(error) { // 请求失败以后的操作
console.log(error);
})
})
</script>
结论:
# 结论:写ajax的时候 你可以直接将dataType参数加上 以防万一 或者后端就用JsonResonse
$.ajax({
url:'', # 朝后端哪个地址发送 跟action三种书写方式一致
type:'get/post', # 提交方式 默认get 跟form表单method参数一致
dataType:'JSON',
data:{'username':'jason','password':123}, # 要发送的数据
success:function(args){
# 异步回调处理机制
}
})
小案例:
在不刷新的情况下显示计算结果到页面上,页面输入两个整数,通过AJAX传输到后端计算出结果并返回。(也不能在前端计算)
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ajax
url(r'^ab_ajax/', views.ab_ajax),
]
urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
def ab_ajax(request): # 和ajax交互的时候,无论返回什么,都不会作用于浏览器,而是交给ajax的回调函数
if request.method == "POST":
# print(request.POST) # <QueryDict: {'i1':['123'],'i2':['345']}>
i1 = request.POST.get("i1")
i2 = request.POST.get("i2")
i3 = int(i1) + int(i2)
return HttpResponse(i3)
return render(request,"index.html")
views.py
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<input type="text" id="d1"> +
<input type="text" id="d2"> =
<input type="text" id="d3">
<p>
<button id="btn">点我</button>
</p>
<script>
// 先给按钮绑定一个点击事件
$("#btn").click(function () {
// 朝后端发送ajax请求
$.ajax({
// 1、指定朝哪个后端发ajax请求
url:"", // 不写就是朝当前地址提交
// 2、请求方式
type:"post", //不指定默认就是get,都是小写,用ajax提交post数据,后端也是在request.POST中接收
// 3、数据
data:{"i1":$("#d1").val(),"i2":$("#d2").val()},
// 4、回调函数:当后端给你返回结果的时候会自动触发 args接收后端的返回结果
success:function (args) {
$("#d3").val(args) // 通过DOM操作动态渲染到第三个input里面
}
})
})
</script>
</body>
</html>
index.html
补充:如果视图函数返回字典需要序列化
# 针对后端如果是用HttpResponse返回的数据(json模块序列化后) 回调函数不会自动帮你反序列化
# 如果后端直接用的是JsonResponse返回的数据 回调函数会自动帮你反序列化
# HttpResponse解决方式
1.自己在前端利用JSON.parse(args),手动反序列化
2.在ajax里面配置一个参数
dataType:"json",
示例1:后端使用JsonResponse返回数据
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ajax
url(r'^ab_ajax/', views.ab_ajax),
]
urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
# Create your views here.
def ab_ajax(request): # 和ajax交互的时候,无论返回什么,都不会作用于浏览器,而是交给ajax的回调函数
if request.method == "POST":
# print(request.POST)
i1 = request.POST.get("i1")
i2 = request.POST.get("i2")
i3 = int(i1) + int(i2)
d = {"code":100,"msg":i3}
return JsonResponse(d)
return render(request,"index.html")
views.py
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<input type="text" id="d1"> +
<input type="text" id="d2"> =
<input type="text" id="d3">
<p>
<button id="btn">点我</button>
</p>
<script>
// 先给按钮绑定一个点击事件
$("#btn").click(function () {
// 朝后端发送ajax请求
$.ajax({
// 1、指定朝哪个后端发ajax请求
url:"", // 不写就是朝当前地址提交
// 2、请求方式
type:"post", //不指定默认就是get,都是小写,用ajax提交post数据,后端也是在request.POST中接收
// 3、数据
data:{"i1":$("#d1").val(),"i2":$("#d2").val()},
// 4、回调函数:当后端给你返回结果的时候会自动触发 args接收后端的返回结果
success:function (args) {
{#$("#d3").val(args) // 通过DOM操作动态渲染到第三个input里面#}
console.log(typeof args) // 接收到的是对象类型
}
})
})
</script>
</body>
</html>
index.html
示例2:后端用HttpResponse返回的数据(json序列化后的数据)
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ajax
url(r'^ab_ajax/', views.ab_ajax),
]
urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect
import json
# Create your views here.
def ab_ajax(request): # 和ajax交互的时候,无论返回什么,都不会作用于浏览器,而是交给ajax的回调函数
if request.method == "POST":
# print(request.POST)
i1 = request.POST.get("i1")
i2 = request.POST.get("i2")
i3 = int(i1) + int(i2)
d = {"code":100,"msg":i3}
return HttpResponse(json.dumps(d))
return render(request,"index.html")
views.py
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<input type="text" id="d1"> +
<input type="text" id="d2"> =
<input type="text" id="d3">
<p>
<button id="btn">点我</button>
</p>
<script>
// 先给按钮绑定一个点击事件
$("#btn").click(function () {
// 朝后端发送ajax请求
$.ajax({
// 1、指定朝哪个后端发ajax请求
url:"", // 不写就是朝当前地址提交
// 2、请求方式
type:"post", //不指定默认就是get,都是小写,用ajax提交post数据,后端也是在request.POST中接收
// 3、数据
data:{"i1":$("#d1").val(),"i2":$("#d2").val()},
// 4、回调函数:当后端给你返回结果的时候会自动触发 args接收后端的返回结果
dataType:"json",
success:function (args) {
{#$("#d3").val(args) // 通过DOM操作动态渲染到第三个input里面#}
console.log(typeof args) // 接收到的是对象类型
}
})
})
</script>
</body>
</html>
index.html
朝后端传输数据的编码格式(contentType)
# 我们主要研究post请求数据的编码格式
"""
get请求数据就是直接放在url后面的
url?username=jason&password=123
"""
可以朝后端发送post请求的方式
# 1.form表单
# 2.ajax请求
form表单朝后端发送post请求的编码格式
朝后端传输数据的编码格式
# 1.urlencoded
# 2.formdata
# 3.json
知识点:
# 默认的数据编码格式是urlencoded
数据格式:username=jason&password=123
django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中
username=jason&password=123 >>> request.POST
# 如果你把编码格式改成formdata,那么针对普通的键值对还是解析到request.POST中而将文件解析到request.FILES中
form表单是没有办法发送json格式数据的
查看数据编码格式:
查看数据格式:
ajax朝后端发送post请求的编码格式
知识点:
# 默认的编码格式也是urlencoded
数据格式:username=jason&age=20
django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中
username=jason&age=20 >>> request.POST
ajax发送json格式数据
# 宗旨:
前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的
不要骗人家!!!
# ajax朝后端发送的json数据:(注意要先将data数据序列化成json格式字符串)
格式:{"username":"jason","age":25}
后端在request.POST里面肯定找不到
因为django针对json格式的数据 不会做任何的处理,会存放在request.body中(二进制形式)b'{"username":"jason","age":25}'
# request对象方法补充
request.is_ajax()
判断当前请求是否是ajax请求 返回布尔值
正确示例:
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ajax发送json格式数据
url(r'^ab_json/', views.ab_json),
]
urls.py
views.py
from django.shortcuts import render
# Create your views here.
def ab_json(request):
import json
if request.is_ajax(): # 判断当前请求是否是ajax请求 返回布尔值 # 针对json格式数据需要手动处理,django不会做任何处理,存放在request.body
json_bytes = request.body # b'{"username":"jason","age":25}'
# 反序列化方式一:先decode解码再loads反序列化
# json_str = json_bytes.decode('utf-8')
# json_dict = json.loads(json_str)
# 反序列化方式二:json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化
json_dict = json.loads(json_bytes)
print(json_dict,type(json_dict))
return render(request,"ab_json.html")
ab_json.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<button class="btn btn-danger" id="d1">点我</button>
<script>
$("#d1").click(function () {
$.ajax({
url:"",
type:"post",
data:JSON.stringify({"username":"jason","age":20}), // 序列化为json格式字符串
dataType:'JSON', contentType:"application/json", // 编码格式改为json,指定编码格式,默认是urlencoded格式
success:function (args) {
}
})
})
</script>
</body>
</html>
总结:
# ajax发送json格式数据需要注意点
1.contentType参数指定成:application/json
2.数据是真正的json格式数据
3.django后端不会帮你处理json格式数据需要你自己去request.body获取并处理
ajax发送文件
"""
ajax发送文件需要借助于js内置对象FormData
"""
示例:
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# ajax发送文件
url(r'^ab_file/', views.ab_file),
]
views.py
from django.shortcuts import render
# Create your views here.
def ab_file(request):
if request.is_ajax():
if request.method == 'POST':
print(request.POST)
print(request.FILES)
return render(request,"ab_file.html")
ab_file.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
<p>usernmae:<input type="text" id="d1"></p>
<p>password:<input type="text" id="d2"></p>
<p><input type="file" id="d3"></p>
<button class="btn btn-info" id="d4">点我</button>
<script>
// 点击按钮朝后端发送普通键值对和文件数据
$("#d4").on("click",function () {
// 1.需要先利用FormData内置对象
let formDateObj = new FormData();
// 2.添加普通的键值对
formDateObj.append('username',$('#d1').val());
formDateObj.append('password',$('#d2').val());
// 3.添加文件对象,$('#d3')[0]先将jQuery对象转换为DOM对象
formDateObj.append('myfile',$('#d3')[0].files[0])
// 4.将对象基于ajax发送给后端
$.ajax({
url:'',
type:'post',
data:formDateObj, // 直接将对象放在data后面即可
dataType:'JSON',
// ajax发送文件必须要指定的两个参数
contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象
processData:false, // 告诉你的浏览器不要对你的数据进行任何处理
success:function (args) {
}
})
})
</script>
</body>
</html>
总结:
# 总结:
1.需要利用内置对象FormData
// 2 添加普通的键值对
formDateObj.append('username',$('#d1').val());
formDateObj.append('password',$('#d2').val());
// 3 添加文件对象
formDateObj.append('myfile',$('#d3')[0].files[0])
2.需要指定两个关键性的参数
contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象
processData:false, // 告诉你的浏览器不要对你的数据进行任何处理
3.django后端能够直接识别到formdata对象并且能够将内部的普通键值自动解析并封装到request.POST中 文件数据自动解析并封装到request.FILES中
django自带的序列化组件(drf做铺垫)
"""
如果发现你可以直接使用MySQL但是无法使用sqlite3
不要慌张不要恐惧 你只需要按照之前MySQL的操作将sqlite3的驱动装一下即可
"""
# 需求:在前端给我获取到后端用户表里面所有的数据 并且要是列表套字典
示例:
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 序列化组件相关
url(r'^ab_ser/', views.ab_ser),
]
views.py
from django.shortcuts import render,HttpResponse
from app01 import models
from django.http import JsonResponse
from django.core import serializers # 序列化组件serializers
# Create your views here.
def ab_ser(request):
user_queryset = models.User.objects.all()
# 需求返回[{},{},{},{},{}]格式
# 方法一:手动实现
# user_list = []
# for user_obj in user_queryset:
# tmp = {
# 'pk':user_obj.pk,
# 'username':user_obj.username,
# 'age':user_obj.age,
# 'gender':user_obj.get_gender_display(),
# }
# user_list.append(tmp)
#
# return JsonResponse(user_list,safe=False)
# 方法二:序列化
res = serializers.serialize('json',user_queryset) # 第一个参数:序列化成什么格式,第二个参数:要序列化的数据
return HttpResponse(res)
"""
前后端分离的项目
作为后端开发的你只需要写代码将数据处理好
能够序列化返回给前端即可
再写一个接口文档 告诉前端每个字段代表的意思即可
# 手动实现
[
{
"pk": 1,
"username": "jason",
"age": 20,
"gender": "male"
},
{
"pk": 2,
"username": "egon",
"age": 22,
"gender": "female"
},
{
"pk": 3,
"username": "tank",
"age": 25,
"gender": "male"
},
{
"pk": 4,
"username": "lxx",
"age": 30,
"gender": "other"
}
]
使用serializers序列化
[
{
"model": "app01.user",
"pk": 1,
"fields": {
"username": "jason",
"age": 20,
"gender": 1
}
},
{
"model": "app01.user",
"pk": 2,
"fields": {
"username": "egon",
"age": 22,
"gender": 2
}
},
{
"model": "app01.user",
"pk": 3,
"fields": {
"username": "tank",
"age": 25,
"gender": 1
}
},
{
"model": "app01.user",
"pk": 4,
"fields": {
"username": "lxx",
"age": 30,
"gender": 3
}
}
]
写接口就是利用序列化组件渲染数据然后写一个接口文档 该交代交代一下就完事了
"""
ajax结合sweetalert实现删除按钮的二次确认
"""
自己要学会如何拷贝
学会基于别人的基础之上做修改
研究各个参数表示的意思 然后找葫芦画瓢
"""
示例:
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 用户展示页
url(r'^user/list/', views.user_list),
# 删除用户
url(r'^delete/user/',views.delete_user),
]
views.py
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
import time
# Create your views here.
def user_list(request):
user_queryset = models.User.objects.all()
return render(request,"user_list.html",locals())
def delete_user(request):
"""
前后端在用ajax进行交互的时候 后端通常给ajax的回调函数返回一个字典格式的数据
"""
if request.is_ajax():
if request.method == 'POST':
back_dic = {"code":1000,'msg':''}
time.sleep(3) # 模拟操作数据的延迟
delete_id = request.POST.get('delete_id')
models.User.objects.filter(pk=delete_id).delete()
back_dic['msg'] = '数据已经删了,你赶紧跑路!'
# 我们需要告诉前端我们操作的结果
return JsonResponse(back_dic)
user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="{% static "bootstrap-sweetalert-master/dist/sweetalert.css" %}">
<script src="{% static "bootstrap-sweetalert-master/dist/sweetalert.min.js" %}"></script>
</head>
<body>
<h1 class="text-center">数据展示</h1>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table-striped table table-hover">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>age</th>
<th>gender</th>
<th>actions</th>
</tr>
</thead>
<tbody>
{% for user_obj in user_queryset %}
<tr>
<td>{{ user_obj.pk }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.age }}</td>
<td>{{ user_obj.get_gender_display }}</td>
<td>
<button class="btn btn-primary btn-xs">编辑</button>
<button class="btn btn-danger btn-xs del" delete_id="{{ user_obj.pk }}">删除</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
$('.del').on('click',function () {
// 先将当前标签对象存储起来
let currentBtn = $(this); // this指代当前被操作对象本身
// 二次确认弹框
swal({
title: "你确定要删吗?",
text: "删除可就找不回来了哦!",
type: "warning",
showCancelButton: true, // 是否显示取消按钮
confirmButtonClass: "btn-danger", // 确认按钮的样式类
confirmButtonText: "删除", // 确认按钮文本
cancelButtonText: "取消", // 取消按钮文本
closeOnConfirm: false,
closeOnCancel: false,
showLoaderOnConfirm: true // 显示正在删除的动画效果 // 等待后端处理时间 。。。动画
},
function(isConfirm) { // isConfirm 是用来判断用户是否点了确认
if (isConfirm) {
// 朝后端发送ajax请求删除数据之后 再弹下面的提示框
$.ajax({
{#url:'/delete/user/' + currentBtn.attr('delete_id'), // 1 传递主键值方式1#}
url:'/delete/user/', // 2 方式2,放在data里面
type:'post',
data:{'delete_id':currentBtn.attr('delete_id')},
success:function (args) { // args = {'code':'','msg':''}
// 判断响应状态码 然后做不同的处理
if(args.code === 1000){
swal("删了!", args.msg, "success");
// 1.lowb版本 直接刷新当前页面
{#window.location.reload()#}
// 2.利用DOM操作 动态刷新 通过当前被操作对象,移动到数据那一行对应的标签,删除行标签
currentBtn.parent().parent().remove()
}else{
swal('完了','出现了未知的错误','info')
}
}
})
} else {
swal("怂逼", "不要说我认识你", "error");
}
});
})
</script>
</body>
</html>
注意:上面代码在删除时有个bug,中文汉字被覆盖
解决方法:
1、先找到被遮挡文字的标签位置
2、在user_list.html页面中添加内部css样式表,为找到的标签添加一个上填充padding
<style>
div.sweet-alert h2 {
padding-top: 10px;
}
</style>
不使用sweetAlert的思路
res = confirm()
if(res){
$.ajax({})
}else{
不发
}
批量插入
需求:
# 访问url的时候
# 先Book表插入1000条数据
# 再将说有的数据查询并展示到前端页面
low版方法:使用for循环一条条插入,插入1000条
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 批量插入
url(r'^ab_batch',views.ab_batch),
]
urls.py
views.py
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
# Create your views here.
def ab_batch(request):
book_list = []
for i in range(10000):
book_obj = models.Book(title="第%s本书" %i)
book_list.append(book_obj)
# 上面两行可以直接缩写成一行
# 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_batch.html",locals())
views.py
ab_batch.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
{% for book_obj in book_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
</body>
</html>
ab_batch.html
总结:
# 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间
分页器推导
"""
总数据100 每页展示10 需要10
总数据101 每页展示10 需要11
总数据99 每页展示10 需要10
如何通过代码动态的计算出到底需要多少页?
在制作页码个数的时候 一般情况下都是奇数个 符合对称美的标准
"""
初步推导:
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 分页
url(r'^ab_batch',views.ab_batch),
]
views.py
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
def ab_batch(request):
# 分页器推导
book_list = models.Book.objects.all()
# 想访问哪一页
current_page = request.GET.get('page', 1) # 如果获取不到当前页码 就展示第一页
# 数据类型转换
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 每页展示多少条
per_page_num = 10
# 起始位置
start_page = (current_page - 1) * per_page_num
# 终止位置
end_page = current_page * per_page_num
# 计算出到底需要多少页
all_count = book_list.count()
page_count, more = divmod(all_count, per_page_num)
if more:
page_count += 1
if current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (current_page - 1,)
page_html = ''
current_page_new = current_page
if current_page < 6:
current_page = 6
for i in range(current_page - 5, current_page + 6):
if current_page_new == i:
page_html += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i)
else:
page_html += '<li><a href="?page=%s">%s</a></li>' % (i, i)
if current_page >= page_count:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (current_page + 1,)
book_queryset = book_list[start_page:end_page]
return render(request,"ab_batch.html",locals())
'''
per_page_num = 10
current_page start_page end_page
1 0 10
2 10 20
3 20 30
4 30 40
per_page_num = 5
current_page start_page end_page
1 0 5
2 5 10
3 15 20
4 25 30
规律:
start_page = (current_page - 1) * per_page_num
end_page = current_page * per_page_num
'''
ab_batch.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
{% for book_obj in book_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
<div class="text-center">
<nav aria-label="Page navigation">
<ul class="pagination ">
<li><a href="?page=1">首页</a></li>
{{ prev_page|safe }}
{{ page_html|safe }}
{{ next_page|safe }}
<li><a href="?page={{ page_count }}">尾部</a></li>
</ul>
</nav>
</div>
</body>
</html>
推导过程
1.queryset对象是直接切片操作的
2.用户到底要访问哪一页 如何确定? url?page=1
current_page = request.GET.get('page',1)
# 获取到的数据都是字符串类型 你需要注意类型转换
3.自己规定每页展示多少条数据
per_page_num = 10
4.切片的起始位置和终止位置
start_page = (current_page - 1)* per_page_num
end_page = current_page * per_page_num
# 利用简单找规律 找出上述四个参数的规律
5.当前数据的总条数
book_queryset.count()
6.如何确定总共需要多少页才能展示完所有的数据
# 利用python内置函数divmod()
page_count, more = divmod(all_count,per_page_num)
if more:
page_count += 1
7.前端模版语法是没有range功能的
# 前端代码不一定非要在前端书写 也可以在后端生成传递给页面
8.针对需要展示的页码需要你自己规划好到底展示多少个页码
# 一般情况下页码的个数设计都是奇数(符合审美标准) 11个页码
当前页减5
当前页加6
你可以给标签价样式从而让选中的页码高亮显示
9.针对页码小于6的情况 你需要做处理 不能再减
终极版分页器
初级版是自定义分页器开发流程的基本思路,我们不需要掌握代码的编写,只需要掌握基本用法即可
自定义分页器封装代码
注意:
自定义的分页器是基于bootstrap样式来的 所以需要提前导入bootstrap
bootstrap 版本 v3
jQuery 版本 v3
# 分页器
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
: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>'
<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('''
</ul>
</nav>
''')
return ''.join(page_html_list)
自定义分页器拷贝及使用
"""
当我们需要使用到非django内置的第三方功能或者组件代码的时候
我们一般情况下会在项目根目录创建一个名为utils文件夹 在该文件夹内对模块进行功能性划分
utils也可以可以在每个应用下创建 具体结合实际情况
我们到了后期封装代码的时候 不再局限于函数
还是尽量朝面向对象去封装
我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap
bootstrap 版本 v3
jQuery 版本 v3
"""
1、在django项目根目录或app01应用目录新建一个utils文件夹用于存放第三方功能或模块
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 分页
url(r'^ab_batch',views.ab_batch),
]
mypage.py
# 分页器
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
: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>'
<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('''
</ul>
</nav>
''')
return ''.join(page_html_list)
mypage.py
views.py后端使用
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
from utils.mypage import Pagination # 导入分页器,这里utils文件夹在根目录下,如果在app01应用目录下使用from app01.utils.mypage import Pagination
def ab_batch(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, 'ab_batch.html', locals())
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap3 核心 CSS 文件 -->
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- Bootstrap3 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- font-awesome.min.css图标库4.7版本 -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
{% for book_obj in book_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
<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 %} <div class="text-center">
{{ page_obj.page_html|safe }} </div>
</div>
</div>
</div>
</body>
</html>
Django自带的分页器(paginator)简介
在页面显示分页数据,需要用到Django分页器组件
from django.core.paginator import Paginator
Paginator对象: paginator = Paginator(user_list, 10)
# per_page: 每页显示条目数量
# count: 数据总个数
# num_pages:总页数
# page_range:总页数的索引范围,如: (1,10),(1,200)
# page: page对象
page对象:page=paginator.page(1)
# has_next 是否有下一页
# next_page_number 下一页页码
# has_previous 是否有上一页
# previous_page_number 上一页页码
# object_list 分页之后的数据列表
# number 当前页
# paginator paginator对象
应用view层
from django.shortcuts import render,HttpResponse
# Create your views here.
from app01.models import *
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def index(request):
'''
批量导入数据:
Booklist=[]
for i in range(100):
Booklist.append(Book(title="book"+str(i),price=30+i*i))
Book.objects.bulk_create(Booklist)
'''
'''
分页器的使用:
book_list=Book.objects.all()
paginator = Paginator(book_list, 10)
print("count:",paginator.count) #数据总数
print("num_pages",paginator.num_pages) #总页数
print("page_range",paginator.page_range) #页码的列表
page1=paginator.page(1) #第1页的page对象
for i in page1: #遍历第1页的所有数据对象
print(i)
print(page1.object_list) #第1页的所有数据
page2=paginator.page(2)
print(page2.has_next()) #是否有下一页
print(page2.next_page_number()) #下一页的页码
print(page2.has_previous()) #是否有上一页
print(page2.previous_page_number()) #上一页的页码
# 抛错
#page=paginator.page(12) # error:EmptyPage
#page=paginator.page("z") # error:PageNotAnInteger
'''
book_list=Book.objects.all()
paginator = Paginator(book_list, 10)
page = request.GET.get('page',1)
currentPage=int(page)
try:
print(page)
book_list = paginator.page(page)
except PageNotAnInteger:
book_list = paginator.page(1)
except EmptyPage:
book_list = paginator.page(paginator.num_pages)
return render(request,"index.html",{"book_list":book_list,"paginator":paginator,"currentPage":currentPage})
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h4>分页器</h4>
<ul>
{% for book in book_list %}
<li>{{ book.title }} -----{{ book.price }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if book_list.has_previous %}
<li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for num in paginator.page_range %}
{% if num == currentPage %}
<li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if book_list.has_next %}
<li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</div>
</body>
</html>
扩展
核心逻辑
'''
显示左5,右5,总共11个页,
1 如果总页码大于11
1.1 if 当前页码减5小于1,要生成1到12的列表(顾头不顾尾,共11个页码)
page_range=range(1,12)
1.2 elif 当前页码+5大于总页码,生成当前页码减10,到当前页码加1的列表(顾头不顾尾,共11个页码)
page_range=range(paginator.num_pages-10,paginator.num_pages+1)
1.3 else 生成当前页码-5,到当前页码+6的列表
page_range=range(current_page_num-5,current_page_num+6)
2 其它情况,生成的列表就是pageinator的page_range
page_range=paginator.page_range
'''
使用案例:
视图层
def page_test(request):
# book_list=[]
# for i in range(100):
# book=Book(name='book%s'%i,price=10+i,pub_date='2018-09-18',publish_id=1)
# book_list.append(book)
# Book.objects.bulk_create(book_list,10)
book_list=Book.objects.all()
# 生成paginator对象,传入书籍列表,每页10条数据
paginator=Paginator(book_list,3)
# 总页码数
print(paginator.num_pages)
# 页码列表
print(paginator.page_range)
# 总数据
print(paginator.count)
# 获取页面传来的页码
current_page=int(request.GET.get('page',1))
page_range=[]
# 左5 右5
# 获取页面传来的页码的page对象
try:
page=paginator.page(current_page)
# print(page.has_next()) #是否有下一页
# print(page.next_page_number()) #下一页的页码
# print(page.has_previous()) #是否有上一页
# print(page.previous_page_number()) #上一页的页码
# 循环打印出当页对象
for i in page:
print(i)
except Exception as e:
current_page=1
page = paginator.page(1)
if paginator.num_pages>11:
if current_page+5>paginator.num_pages:
page_range=range(paginator.num_pages-10,paginator.num_pages+1)
elif current_page-5<1:
page_range=range(1,12)
else:
page_range=range(current_page-5,current_page+6)
else:
page_range=paginator.page_range
return render(request,'page_test.html',locals())
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<title>Title</title>
</head>
<body>
<ul>
{% for foo in page %}
<li>{{ foo.name }}</li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page.has_previous %}
<li>
<a href="/page_test/?page={{ page.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
</li>
{% endif %}
{% for foo in page_range %}
{% if current_page == foo %}
<li class="active"><a href="/page_test/?page={{ foo }}">{{ foo }}</a></li>
{% else %}
<li><a href="/page_test/?page={{ foo }}">{{ foo }}</a></li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li>
<a href="/page_test/?page={{ page.next_page_number }}" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</body>
</html>