1. 提交数据方式
请求的方式:
1. 浏览器url GET请求
2. a便签 href属性 GET请求
3. form表单 GET/POST请求
4. Ajax GET/POST请求
2. Ajax
Ajax的作用:异步提交,局部刷新.
优点:在不重新加载整个页面的情况下,可以与服务器交互数据并更新网页中的内容.
应用:
网站的注册,动态的获取用户输入,实时的与后端确定,并实时展示到前端(局部刷新).
80% 前后端交互都是Ajax,模块不一样,内容一样.
Django中使用的不是原生的Ajax.使用jQuery封装之后的版本.
使用Ajax时需要先导入jQuery.
2.1 固定语法
$.ajax({
url:'',
type:'post',
data:{'username':'kid', 'password':123},
success:function (args){
"""
在利用ajax进行前后端交互的时候
后端无论返回什么,都自会被回调函数接受,而不影响页面
"""
}
})
只要是通过Ajax访问的,所有返回的数据都被回调函数的参数args接收.
2.2 Ajax交互
简易的计算器:页面上有三个input款,
在前端中输入数字,点击按钮,发送ajax请求.
后端计算结果,在返回给前端动态展示到第三个框中,页面不许刷新.
1. 路由层
url(r'^add_ajax/', views.add_ajax),
2. 视图层
from django.shortcuts import render, HttpResponse, redirect
def add_ajax(request):
return render(request, 'add_ajax.html')
3. 前端页面
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算器title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<p>
<input type="text" id="i1"> +
<input type="text" id="i2"> =
<input type="text" id="i3">
<input type="submit" id="btn">
p>
div>
<script>
$('#btn').click(function () {
$.ajax({
url: '',
type: 'post',
data: {'i1': $('#i1').val(), 'i2': $('#i2').val()},
success: function (args) {
$('#i3').val(args)
}
})
})
script>
body>
html>
.val() 获取值
.val(args) 写入值
4. 业务逻辑
from django.shortcuts import render, HttpResponse, redirect
def add_ajax(request):
if request.method == 'POST':
post_obj = request.POST
print(post_obj)
i1 = post_obj.get('i1')
i2 = post_obj.get('i2')
print(i1, type(i1), i2, type(i2))
i3 = int(i1) + int(i2)
return HttpResponse(i3)
return render(request, 'add_ajax.html')
2.3 数据类型
使用 HttpResponse 返回的数据都会被转为字符串.
console.log(typeof args);
数据类型不想被转换为字符串类型的数据,可以发送json格式数据.
在前端在反序列化得到Python 对应 js的数据类型.
后端用HttpResponse返回数据,回调函数需要手动反序列化.
后端用JsonResponse返回的数据,回调函数会自动反序列化.
1.1 前端利用JSON.parse()
success:function (args){
data = JSON.parse(args)
···
1.2 在ajax前配置一个参数 dataType:'json',
$.ajax({
···
dataType:'json',
···
2. JsonResponse 返回数据
from django.http inport JsonResponse
return JsonResponse('xx')
返回json格式字符串
JsonResponse 返回json格式字符串
ajax访问之后一般返回一个字典,js中就是一个对象,直接通过点的方式取值.
字典的格式:
{'code': 200, 'msg':'xxx'}
响应状态码 响应体数据 ...
前端可以通过状态码再进行一轮判断在进行操作...
3. form编码格式
get 请求:请求的数据就是直接放在url后面:
url?username=kid&password=123 字符串
post 请求:
可以向后端发送post请求的方式:
1. form表单
2. ajax请求
编码格式:
1. urlencoded
2. formdata
form表单提交数据,默认的数据编码格式是urlencoded
django后端针对符合urlencoded 编码格式的数据都会封装到request.POST中.
form-data 针对普通的值还是解析到request.POST中,而文件则解析解析到request.FILES中.
3.1 urlencoded格式
form表单默认提交的是urlencoded格式数据.
url(r'^form_data', views.form_data),
def form_data(request):
return render(request, 'form_data.html')
<html lang="en">
<head>
<meta charset="UTF-8">
<title>form表单title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="" method="post">
<p>
<label for="username">用户:
<input type="text" id="username" name="username" class="form-control form-group-lg">
label>
p>
<p>
<label for="password">密码:
<input type="text" id="password" name="password" class="form-control form-group-lg">
label>
p>
<p>
<input type="submit" class="btn btn-primary">
<p>
form>
div>
body>
html>
0. 打开开发者工具选择网络.
1. url中输入 127.0.0.1:8000/form_data
2. 输入账户密码,向后端发送请求.
3. 在浏览器中查看index的数据格式.
表单数据:是美化后的数据
真正的数据是
username=kid&password=123
3.2 form-data格式
form表单传输文件必须设置的数据格式:multipart/formdata,
后端使用request.FILES接收文件.
0. 在form表单中添加一个input标签 type=file.
1. 设置form表单提交的数据格式 enctype="multipart/form-data
<div>
<form action="" method="post" enctype="multipart/form-data">
<label for="file">
<input type="file" id="file" name="file">
label>
<input type="submit" class="btn btn-primary">
form>
div>
获取文件数据 request.FILES
def form_data(request):
if request.method == 'POST':
file_obj = request.FILES
print(file_obj)
print(request.POST)
return render(request, 'form_data.html')
<MultiValueDict: {'file': []}>
<QueryDict: {'username': ['kid'], 'password': ['123']}>
浏览器中显示的格式
Conten t-Type: multipart/form-data; boundary=----WebKitFormBoundaryDRlTkP2zKWD57WTZ
将enctype="multipart/form-data"去掉
浏览器中显示的格式 Content-Type: application/x-www-form-urlencoded
提交文件的时候 request.FILES 获取不到文件,
<MultiValueDict: {}>
request.POST 获取到文件的文件名
4. Ajax提交数据
4.1 urlencoded格式
Ajax提交方式选择 post
现在input框是在form表单中, 按下submit按钮触发绑定事件发送了Ajax之后
,还会提交form表单的请求.
在ajax中阻止submit的后续事件.
<html lang="en">
<head>
<meta charset="UTF-8">
<title>form表单title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="" enctype="multipart/form-data">
<p>
<label for="username">用户:
<input type="text" id="username" name="username" class="form-control form-group-lg">
label>
p>
<p>
<label for="password">密码:
<input type="text" id="password" name="password" class="form-control form-group-lg">
label>
p>
<p>
<label for="file">
<input type="file" id="file" name="file">
label>
p>
<p>
<input type="submit" class="btn btn-primary">
<p>
form>
div>
<script>
$(':submit').on('click', function () {
var username = $(':text').val()
var password = $(':password').val()
var file = $(':file').val()
event.preventDefault();
$.ajax({
url: '',
type: 'post',
data: {username: username, password: password, file: file},
success: function (args) {
alert(args)
}
})
})
script>
body>
html>
<MultiValueDict: {}>
<QueryDict: {'username': ['kid'], 'file': ['C:\\fakepath\\bird.png']}>
浏览器中查看格式:
Content-Type: application/x-www-form-urlencoded
默认的编码格式也是urlencoded.Ajax提交POST请求,解析到request.POST中.
4.2 json 格式
ajax发送json格式注意点:
1. contenTYPE参数指定为application/json
2. 数据是真正的json格式数据
3. django后端将json格式数据打包到request.body中.
* 前后端交互数据的时候一定要保 验证数据的类型和格式.
url('^ajax_json', views.ajax_json)
后端获取数据
request.is_ajax() 判断当前请求是否为ajax请求
json格式的数据存放在requuest.body中.
def ajax_json(request):
if request.is_ajax():
ajax_obj = request.body
print(ajax_obj, type(ajax_obj))
return HttpResponse("ok")
return render(request, 'ajax_json.html')
// 指定编码格式
contentType:'application/jsom',
// 序列化数据
data: JSON.stringify()
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax_jsontitle>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="" enctype="multipart/form-data">
<p>
<label for="username">用户:
<input type="text" id="username" name="username" class="form-control form-group-lg">
label>
p>
<p>
<label for="password">密码:
<input type="text" id="password" name="password" class="form-control form-group-lg">
label>
p>
<p>
<label for="file">
<input type="file" id="file" name="file">
label>
p>
<p>
<input type="submit" class="btn btn-primary">
<p>
form>
div>
<script>
$(':submit').on('click', function () {
var username = $(':text').val()
var password = $(':password').val()
var file = $(':file').val()
event.preventDefault();
$.ajax({
url: '',
type: 'post',
data: JSON.stringify({username: username, password: password, file: file}),
success: function (args) {
alert(args)
}
})
})
script>
body>
html>
# 返回json格式字符串 手处理反序列化得到对应的数据类型
b'{"username":"kid","file":"C:\\\\fakepath\\\\bird.png"}' <class 'bytes'>
{'username': 'kid', 'file': 'C:\\fakepath\\bird.png'} <class 'dict'>
浏览器中查看
Content-Type: application/jsom
4.3 文件
ajax发送文件需要借助于js内置对象ForData.
1.需要利用FormData内置对象
let ForData对象 = new FormData();
// 添加普通的键值对
formDataobj.append('键', 值);
// 添加文件对象
formDataobj.append('键', $('')[0].files[0])
2.ajax发送文件必须要指定两个参数
contentType:false, // 不需要任何编码django能自动识别fordata对象
processData:false, // 告诉浏览器不要对数据进行任何处理
3.django后端能直接识别带formdata对象并将
普通的键值对自动解析并封装到request.POST中文件自动解析并封装到request.FILES中.
url('^ajax_file', views.ajax_file)
def ajax_file(request):
if request.is_ajax():
if request.method == 'POST':
file_obj = request.POST
print(file_obj, type(file_obj))
file = request.FILES.get('file')
print(file, type(file))
with open(file.name, mode='wb') as wf:
for i in file:
wf.write(i)
return HttpResponse('OK')
return render(request, 'ajax_file.html')
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax_filetitle>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
<form action="">
<p>
<label for="username">用户:
<input type="text" id="username" name="username" class="form-control form-group-lg">
label>
p>
<p>
<label for="username">密码:
<input type="password" id="username" name="username" class="form-control form-group-lg">
label>
p>
<p>
<label for="file">
<input type="file" id="file" name="file">
label>
p>
<p>
<label for="submit">
<input type="submit" id="submit" class="btn btn-primary">
label>
p>
form>
div>
<script>
$(':submit').on('click', function () {
var username = $(':text').val()
var password = $(':password').val()
var file = $(':file')[0].files[0]
event.preventDefault();
var file_obj = new FormData()
file_obj.append('username', username)
file_obj.append('password', password)
file_obj.append('file', file)
$.ajax({
url: '',
type: 'post',
data: file_obj,
contentType: false,
processData: false,
success: function (args) {
}
})
})
script>
body>
html>
5. 自动序列化组件
5.1 准备环境
0. 创建一个库设置编码
1. settings.py 中设置数据库的配置
2. __init__.py 中替换库
3. 创建表
4. 数据库迁移
5. 设置测试文件
6. 录数据
mysql> create database file character set utf8;
Query OK, 1 row affected (0.01 sec)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'POST': 3306,
'NAME': 'file',
'USER': 'root',
'PASSWORD': 123,
'CHARSET': 'UTF8'
}
}
import pymysql
pymysql.install_as_MySQLdb()
from django.db import models
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
gender_choices = (
(1, '男'),
(2, '女'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choices)
执行数据库迁移命令:
python manage.py makemigrations
python manage.py migrate
from django.test import TestCase
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django7.settings")
import django
django.setup()
from app01 import models
models.User.objects.create(name='kid', age=18, gender=1)
models.User.objects.create(name='qz', age=19, gender=2)
models.User.objects.create(name='qq', age=20, gender=3)
5.2 用户信息查询
0. 127.0.0.1:8000/view_info
1. 路由层中写路由与视图函数的对应关系
2. 视图函数中获取数据库的数据,传递给前端
3. 前端使用模板语法获取数据
url('^view_info', views.view_info)
def view_info(request):
from app01 import models
queryset_obj = models.User.objects.all()
print(queryset_obj)
return render(request, 'view_info.html', locals())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户数据title>
head>
<body>
<div>
{% for obj in queryset_obj %}
<p>{{ obj.name }} {{ obj.age }} {{ obj.gender }}p>
{% endfor %}
div>
body>
html>
5.3 接口文档
用户查询功能中,后端获取的表中所有的数据.(数据的格式是列表套字典,字典中存的是数据对象.)
接口:
前后端分离的项目时,
后端开发时一般将数据处理成列表套字段的格式,序列化返回给后端,
再写一个接口文档,告诉前端每个字段代表的意思.
而不是直接放回一个对象.
TypeError:为了允许序列化非字典对象,请将安全参数设置为 False。
解决编码问题和序列化非dict对象,请将safe参数设置为False.
return JsonResponse(list,safe=False)
def view_info(request):
from app01 import models
queryset_obj = models.User.objects.all()
info_list = []
for obj in queryset_obj:
info_list.append({'pk': obj.pk, 'name': obj.name, 'age': obj.age, 'gender': obj.get_gender_display()})
print(info_list)
from django.http import JsonResponse
return JsonResponse(info_list, safe=False)
[{'pk': 1, 'name': 'kid', 'age': 18, 'gender': '男'}, {'pk': 2, 'name': 'qz', 'age': 19, 'gender': '女'}, {'pk': 3, 'name': 'qq', 'age': 20, 'gender': '其他'}]
5.3 serializers模块
serializers序列化模块
将上述代码简化,序列化并省去封装数据的步奏.
from django.core import serializers
def view_info(request):
from app01 import models
queryset_obj = models.User.objects.all()
from django.core import serializers
queryset_obj = serializers.serialize('json', queryset_obj)
print(queryset_obj)
return HttpResponse(queryset_obj, safe=False)
[{"model": "app01.user", "pk": 1, "fields": {"name": "kid", "age": 18, "gender": 1}}, {"model": "app01.user", "pk": 2, "fields": {"name": "qz", "age": 19, "gender": 2}}, {"model": "app01.user", "pk": 3, "fields": {"name": "qq", "age": 20, "gender": 3}}]
接口文件: 写一些描述信息.
[
{
"model": "app01.user",
"pk": 1,
"fields": {
"name": "kid",
"age": 18,
"gender": 1
}
},
......
]
1对应男
2对应女
* 针对 choices 参数 serialize 获取的是编号不是真实的值
···
6. 二次确认操作
6.1 sweetalert
ajax结合sweetalert实现二次确认.
sweetalert网址:
http://lipis.github.io/bootstrap-sweetalert/
需要 sweetalert框架的文件.
6.2 提交主键&刷新数据
向后端发送ajax请求删除数据,需要携带数据的id:
方式1: url/数据的主键
url:'/delete/user' + currentBtn.attr('del_id'), // 传递主键值方式1
方式2: 放在请求体内
data:{'del_id': currentBtn.attr('del_id')},
删除数据后剧本刷新:
方式1:
window.location.reload()
方式2:
利用DOM操作动态刷新 当前标签的父标签的父标签
操作对象.parent().parent().remove()
6.3 存在的问题
<style>
div.sweetalert h2{
padding-top: 10px;
}
style>
6.4 注意点
自己写的bug
6.5 完整代码
url('^view_info/', views.view_info),
url('^delete_user/', views.delete_user),
def view_info(request):
from app01 import models
queryset_obj = models.User.objects.all()
return render(request, 'view_info.html', locals())
def delete_user(request):
if request.is_ajax():
delete_id = request.POST.get('del_id')
from app01 import models
delete_num = models.User.objects.filter(pk=delete_id).delete()
print(delete_num)
if delete_num[0] == 0:
data = {'code': 4000, 'msg': '没有对应的数据'}
else:
data = {'code': 2000, 'msg': '删除成功'}
from django.http import JsonResponse
return JsonResponse(data)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户数据title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<script src="{% static 'dist/sweetalert.js' %}">script>
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table-striped table table-hover">
<thead>
<tr>
<th>idth>
<th>姓名th>
<th>年龄th>
<th>性别th>
<th>操作th>
tr>
thead>
<tbody>
{% for foo in queryset_obj %}
<tr>
<td>{{ foo.pk }}td>
<td>{{ foo.name }}td>
<td>{{ foo.age }}td>
<td>{{ foo.get_gender_display }}td>
<td>
<a class="btn btn-primary btn-sm">0.0a>
<a class="btn btn-danger btn-sm del" del_id="{{ foo.pk }}">删除a>
td>
tr>
{% endfor %}
tbody>
table>
div>
div>
div>
<script>
$('.del').on('click', function () {
delete_obj = $(this)
swal({
title: "你确定要删除吗?",
text: "会将你的数据全部清除",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn-danger",
confirmButtonText: "确定",
cancelButtonText: "取消!",
showLoaderOnConfirm: true,
closeOnConfirm: false,
closeOnCancel: false
},
function (isConfirm) {
if (isConfirm) {
$.ajax({
url: '/delete_user/',
type: 'post',
data: {'del_id': delete_obj.attr('del_id')},
success: function (args) {
if (args.code === 2000) {
setTimeout(function () {
swal("确定", args.msg, "success");
delete_obj.parent().parent().remove()
}, 2000);
} else if (args.code === 4000) {
swal("异常", args.msg, "error");
}
}
})
} else {
swal("取消", "你已经撤销操作:)", "success");
}
});
})
script>
body>
html>
7. 批量插入数据
class Book1(models.Model):
title = models.CharField(max_length=32, verbose_name='书名')
class Book2(models.Model):
title = models.CharField(max_length=32, verbose_name='书名')
执行数据库迁移命令:
python manage.py makemigrations
python manage migrate
url('^add_info1', views.add_info1),
url('^add_info2', views.add_info2),
from app01 import models
def add_info1(request):
import time
old_time = time.time()
for i in range(1000):
models.Book1.objects.create(title=f'第{i}本书')
new_time = time.time()
print('数据写入时间: %s' % (new_time - old_time))
query_set = models.Book1.objects.all()
return render(request, 'book_list.html', locals())
def add_info2(request):
import time
old_time = time.time()
book_list = []
for i in range(1000):
book_obj = models.Book2(title=f'第{i}本书')
book_list.append(book_obj)
models.Book2.objects.bulk_create(book_list)
new_time = time.time()
print('数据写入时间: %s' % (new_time - old_time))
query_set = models.Book2.objects.all()
return render(request, 'book_list.html', locals())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图书列表title>
head>
<body>
<table>
<thead>
<tr>
<th>idth>
<th>titleth>
tr>
thead>
<tbody class="form-control form-group-lg">
{% for foo in query_set %}
<tr>
<td>{{ foo.pk }}td>
<td>{{ foo.title }}td>
tr>
{% endfor %}
tbody>
table>
body>
html>
orm内置bulk_create能够减少操作时间.
8. 自定义分页
8.1 分页
制作测试环境.
models.Book1.objects.filter(pk__gt=100).delete()
分析:
数据全部展示在一页是不实际的,将数据分页展示.
1. 每页展示多少条
QuerySet对象 是一个列表套字典的格式, 支持切片操作
每页展示10条数据
per_page_num = 10 一页十条
页码 起始 终止
current_page start_page end_page
1 0 10 展示的数据 0 - 9
2 10 20 展示的数据 10 - 19
3 20 30 展示的数据 20 - 29
2. 获取访问的页码
使用get请求携带访问页码 127.0.0.1:8000/?page=1
current_page = request.GET.get('page', 1) 如果获取不到页面的参数就展示第一页
* url?后面的数据是字符串
3. 计算公式
当前页面 current_page = reuqest.GET.get('page', 1)
每页数 per_page_num = 10
start_page = (page_id - 1) * per_page_num
end_page = current_page * per_page_num-->10
4. 取数据
book_queryset = models.Book.objects.all()[start_page:end_page]
url('^pager/', views.pager),
def pager(request):
page_id = request.GET.get('page', 1)
print(page_id, type(page_id))
if page_id.isdigit():
page_id = int(page_id)
else:
page_id = 1
per_page_num = 10
start_page = (page_id - 1) * per_page_num
end_page = page_id * per_page_num
queryset_obj = models.Book1.objects.all()[start_page: end_page]
return render(request, 'pager.html', locals())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页器title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<script src="{% static 'dist/sweetalert.js' %}">script>
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
{% for foo in queryset_obj %}
<p>{{ foo.pk }} {{ foo.title }}p>
{% endfor %}
div>
body>
html>
http://127.0.0.1:8000/pager/?page=1 访问第一页
http://127.0.0.1:8000/pager/?page=2 访问第一页
8.2 计算总页码
总数据 99条 每页10 需要 10页
总数据 100条 每页10 需要 10页
总数据 101条 每页10 需要 11页
通过divmod() 函数 动态的计算出多少页,
divmod(100, 10)
商 余数
(10, 0)
divmod(101, 10)
(10, 1)
divmod(99, 10)
(9, 9)
余数不为0加一页
def pager(request):
page_id = request.GET.get('page', 1)
print(page_id, type(page_id))
if page_id.isdigit():
page_id = int(page_id)
else:
page_id = 1
all_queryset_obj = models.Book1.objects.all()
per_page_num = 10
page_num, residue = divmod(all_queryset_obj.count(), per_page_num)
if residue:
page_num += 1
page_num_tuple = range(page_num + 1)
if page_id not in page_num_tuple:
page_id = 1
start_page = (page_id - 1) * per_page_num
end_page = page_id * per_page_num
queryset_obj = models.Book1.objects.all()[start_page: end_page]
return render(request, 'pager.html', locals())
8.3 页码器
在制作页面的时候一般都是奇数个符合对称审美.
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页器title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<script src="{% static 'dist/sweetalert.js' %}">script>
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
{% for foo in queryset_obj %}
<p>{{ foo.pk }} {{ foo.title }}p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
<li><a href="?page=1">1a>li>
<li><a href="?page=2">2a>li>
<li><a href="?page=3">3a>li>
<li><a href="?page=4">4a>li>
<li><a href="?page=5">5a>li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
ul>
nav>
div>
body>
html>
8.4 分页器计算
动态产生: <li><a href="?page=1">1</a></li>
统计总页数,产生对应的a标签.
在后端写html代码.
后端:
h1 = 'xxx
'
|safe 前端转义:
<p>{{ h1|safe }}</p>
def pager(request):
page_id = request.GET.get('page', 1)
print(page_id, type(page_id))
if page_id.isdigit():
page_id = int(page_id)
else:
page_id = 1
all_queryset_obj = models.Book1.objects.all()
per_page_num = 10
page_num, residue = divmod(all_queryset_obj.count(), per_page_num)
if residue:
page_num += 1
page_num_tuple = range(page_num + 1)
if page_id not in page_num_tuple:
page_id = 1
pager_html = ''
for i in range(1, page_num + 1):
pager_html += f'{i}'
print(pager_html)
start_page = (page_id - 1) * per_page_num
end_page = page_id * per_page_num
queryset_obj = models.Book1.objects.all()[start_page: end_page]
return render(request, 'pager.html', locals())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页器title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<script src="{% static 'dist/sweetalert.js' %}">script>
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
{% for foo in queryset_obj %}
<p>{{ foo.pk }} {{ foo.title }}p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{{ pager_html|safe }}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
ul>
nav>
div>
body>
html>
展示分页器全部的按钮.
8.5 分页器规划
pager_html = ''
for i in range(1, page_num + 1):
pager_html += f'{i}'
print(pager_html)
设置只展示5个, (一般都显示的都为奇数的)
url?page=3
page_id - 2 page_id - 1 page_id page_id + 1 page_id + 2
1 2 3 4 5
高亮当前行 为li标签 设置 class='active'
range(page_id-2, page_id+3)
pager_html = ''
for i in range(page_id - 2, page_id + 3):
if i == page_id:
pager_html += f'{i}'
else:
pager_html += f'{i}'
左右两边不能超出范围,page_id为 当前页码,page_num总页数.
左边不能为0 右边最大为当前的总页数.
position_num = page_id
if page_id <= 3:
position_num = 3
if page_id + 3 >= page_num:
position_num = page_num - 2
def pager(request):
page_id = request.GET.get('page', 1)
print(page_id, type(page_id))
if page_id.isdigit():
page_id = int(page_id)
else:
page_id = 1
all_queryset_obj = models.Book1.objects.all()
per_page_num = 10
page_num, residue = divmod(all_queryset_obj.count(), per_page_num)
if residue:
page_num += 1
page_num_tuple = range(page_num + 1)
if page_id not in page_num_tuple:
page_id = 1
pager_html = ''
position_num = page_id
if page_id <= 3:
position_num = 3
if page_id + 3 >= page_num:
position_num = page_num - 2
for i in range(position_num - 2, position_num + 3):
if i == page_id:
pager_html += f'{i}'
else:
pager_html += f'{i}'
print(pager_html)
start_page = (page_id - 1) * per_page_num
end_page = page_id * per_page_num
queryset_obj = models.Book1.objects.all()[start_page: end_page]
return render(request, 'pager.html', locals())
9. 分页模板
在使用非django内置的第三方功能或者组件的时候我们一帮情况下个会创建一个
utils文件夹,在该文件夹内对模块进行功能的划分.
utils文件夹也在每个单独的app下创建.
在后期封装代码的时候不在局限与函数,尽量使用面向对象去封装.
将下列代码复制到utils文件夹内的一个自创的文件中.
基于bootstrap样式,需要提前导入.
9.1 模板
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):
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
else:
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
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 = []
page_html_list.append('''
)
first_page = '首页' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '上一页'
else:
prev_page = '上一页' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '%s' % (i, i,)
else:
temp = '%s' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '下一页'
else:
next_page = '下一页' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '尾页' % (self.all_pager,)
page_html_list.append(last_page)
page_html_list.append('''
''')
return ''.join(page_html_list)
9.2 使用模板
url('^pager_model/', views.pager_model)
def pager_model(request):
from app01.utils.page import Pagination
all_queryset_obj = models.Book1.objects.all()
all_queryset_sum = all_queryset_obj.count()
page_num = request.GET.get('page_id', 1)
page_obj = Pagination(
current_page=page_num,
all_count=all_queryset_sum,
per_page_num=10,
pager_count=5,
)
"""
current_page: 当前页 必须的 自动转换类型
all_count: 数据库中的数据总条数 必须的
per_page_num: 每页显示的数据条数 默认2
pager_count: 最多显示的页码个数 默认10
"""
page_queryset = all_queryset_obj[page_obj.start: page_obj.end]
return render(request, 'pager_model.html', locals())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页器模板title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<script src="{% static 'dist/sweetalert.js' %}">script>
<link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
{% for page in page_queryset %}
<p>{{ page.pk }} {{ page.title }}p>
<nav aria-label="Page navigation">nav>
{% endfor %}
{# 利用自动分页器 #}
{{ page_obj.page_html|safe }}
div>
body>
html>