经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python3 » 查看文章
分页查询及其拓展应用案例
来源:cnblogs  作者:顾平安  时间:2024/7/13 18:23:22  对本文有异议

分页查询

分页查询是处理大量数据时常用的技术,通过分页可以将数据分成多个小部分,方便用户逐页查看。SQLAlchemy 提供了简单易用的方法来实现分页查询。

本篇我们也会在最终实现这样的分页效果:

page

1. 什么是分页查询

分页查询是将查询结果按照一定数量分成多页展示,每页显示固定数量的记录。分页查询通常使用两个参数:

  • limit:每页显示的记录数量。
  • offset:跳过的记录数量。

例如,要查询第二页,每页显示 10 条记录:

  • limit:10
  • offset:10

2. 使用 SQLAlchemy 实现分页查询

基本查询

首先,我们需要一个基本的查询来获取数据:

  1. import db
  2. from model import Student
  3. def basic_query():
  4. students = db.session.query(Student).all()
  5. for stu in students:
  6. print(stu.to_dict())

使用 limitoffset

前文中,我们已经了解到 SQLAlchemy 提供了 limitoffset 方法来实现分页查询。limit 限制返回的记录数量,offset 跳过指定数量的记录。

  1. import db
  2. from model import Student
  3. def paginated_query(page, per_page):
  4. q = db.select(Student).limit(per_page).offset((page - 1) * per_page)
  5. students = db.session.execute(q).scalars()
  6. for stu in students:
  7. print(stu.to_dict())

例如,要获取第 2 页,每页显示 10 条记录:

  1. paginated_query(2, 10)

对应的 SQL 语句:

  1. SELECT * FROM tb_student LIMIT 10 OFFSET 10;

3. 前后端实现分页功能

后端分页

在后端实现分页功能时,可以创建一个函数来处理分页逻辑。这个函数接受 pageper_page 参数,并返回当前页的数据和总页数。

  1. import db
  2. from model import Student
  3. def get_paginated_students(page, per_page):
  4. total = db.session.query(Student).count()
  5. q = db.select(Student).limit(per_page).offset((page - 1) * per_page)
  6. students = db.session.execute(q).scalars()
  7. return {
  8. 'total': total,
  9. 'page': page,
  10. 'per_page': per_page,
  11. 'pages': (total + per_page - 1) // per_page,
  12. 'data': [stu.to_dict() for stu in students]
  13. }

前端分页

在前端实现分页时,可以使用后端提供的分页数据来渲染页面:

  1. {
  2. "total": 100,
  3. "page": 2,
  4. "per_page": 10,
  5. "pages": 10,
  6. "data": [
  7. {"id": 11, "name": "Student 11", ...},
  8. {"id": 12, "name": "Student 12", ...},
  9. ...
  10. ]
  11. }

前端可以根据这些数据渲染分页控件和当前页的数据。

[拓展] Flask 分页演示

下面是一个前后端不分离的 Flask 项目,代码文件比较多,你需要自行理一下。同时也要保证 FlaskFlask-SQLAlchemyFlask-MysqlDB 的安装。

  1. pip install flask
  2. pip install flask-sqlalchemy # 兼容 Flask 的 SQLAlchemy 框架,提供 ORM 功能
  3. pip install flask-mysqldb # 为 Flask-SQLAlchemy 提供 MySQL 驱动

Flask 项目目录如下:

  1. flask_app/ # 项目目录
  2. ├── templates/ # 模板目录
  3. └── list.html # 模板文件
  4. ├── config.py # Flask 配置文件
  5. ├── db.py # 数据库核心文件,包含重要操作
  6. ├── manage.py # Flask 路由和业务视图文件
  7. └── models.py # 数据库模型文件

首先看一下配置文件 config.py

  1. class Config:
  2. SQLALCHEMY_DATABASE_URI = 'mysql://root:0908@localhost:3306/db_flask_demo_school?charset=utf8mb4' # 数据库连接。自行替换数据库用户名称和密码以及实际数据库名
  3. SQLALCHEMY_ECHO = False # 是否打印执行的 SQL 语句及其耗时
  4. DEBUG = True # 是否启用调试模式

db.py

  1. """
  2. Create database:
  3. > create database db_flask_demo_school charset=utf8mb4
  4. """
  5. from flask_sqlalchemy import SQLAlchemy
  6. from sqlalchemy import *
  7. db = SQLAlchemy()

models.py

  1. from db import *
  2. class Student(db.Model):
  3. __tablename__ = 'tb_student2'
  4. id = db.Column(db.Integer, primary_key=True, comment="主键")
  5. name = db.Column(db.String(15), index=True, comment="姓名")
  6. age = db.Column(db.SmallInteger, comment="年龄")
  7. sex = db.Column(db.Boolean, comment="性别")
  8. email = db.Column(db.String(128), unique=True, comment="邮箱地址")
  9. money = db.Column(db.Numeric(10, 2), default=0.0, comment="钱包")
  10. def to_dict(self):
  11. return {
  12. 'id': self.id,
  13. 'name': self.name,
  14. 'age': self.age,
  15. 'sex': self.sex,
  16. 'email': self.email,
  17. 'money': float(self.money)
  18. }
  19. def __repr__(self):
  20. return f'<{self.__class__.__name__}: {self.name}>'

然后就是 manage.py,编写了路由与业务代码:

  1. from pathlib import Path
  2. from flask import Flask, jsonify, request, render_template
  3. from config import Config
  4. from models import db, Student
  5. app = Flask(__name__, template_folder='./templates')
  6. app.config.from_object(Config)
  7. db.init_app(app)
  8. @app.route('/', methods=['GET'])
  9. def index():
  10. """没啥用,勿看"""
  11. title = Path(__file__).name
  12. return title
  13. @app.route('/students', methods=['POST'])
  14. def create_student():
  15. """采集访问的信息,创建学生"""
  16. sex = request.form.get('sex')
  17. sex = int(sex) if sex.isdigit() else 0
  18. student = Student(
  19. name=request.form.get('name', '未知'),
  20. age=request.form.get('age', 0),
  21. sex=bool(sex),
  22. email=request.form.get('email', ''),
  23. money=request.form.get('money', 0),
  24. )
  25. if request.form.get('id', None) is not None:
  26. student.id = request.form['id']
  27. db.session.add(student)
  28. db.session.commit()
  29. return jsonify({
  30. 'success': True,
  31. 'data': student.to_dict(),
  32. 'msg': 'success'
  33. }), 201
  34. @app.route('/students', methods=['DELETE'])
  35. def delete_students():
  36. """删除学生表的所有记录"""
  37. db.session.execute(db.delete(Student))
  38. db.session.commit()
  39. return jsonify({
  40. 'success': True,
  41. 'data': None,
  42. 'msg': 'success'
  43. })
  44. @app.route('/students', methods=['GET'])
  45. def get_students():
  46. # 旧版本 2.x 获取全部数据
  47. # students = Student.query.all()
  48. # 新版本 3.1.x 获取全部数据
  49. students = db.session.execute(db.select(Student).where()).scalars()
  50. return jsonify({
  51. 'success': True,
  52. 'data': {
  53. 'count': Student.query.count(),
  54. 'students': [student.to_dict() for student in students]
  55. },
  56. 'msg': 'success'
  57. })
  58. @app.route('/students/<int:student_id>', methods=['GET'])
  59. def get_student(student_id):
  60. # 根据主键查询数据,不存在则为 None
  61. student = db.session.get(Student, student_id)
  62. if not student:
  63. return jsonify({
  64. 'success': False,
  65. 'data': None,
  66. 'msg': 'student not found'
  67. })
  68. return jsonify({
  69. 'success': True,
  70. 'data': student.to_dict(),
  71. 'msg': 'success'
  72. })
  73. @app.route('/students/data', methods=['GET'])
  74. def students_data():
  75. """这里是分页器的使用,不同于我们所使用的 limit 和 offset 需要自己编写"""
  76. # 不采取数据分页时,大量数据时会导致服务器运存膨胀,这是非常不妥的
  77. page = request.args.get('page', 1, type=int)
  78. per_page = request.args.get('size', 3, type=int)
  79. # 创建分页器对象
  80. pagination = Student.query.paginate(page=page, per_page=per_page, max_per_page=20)
  81. print('当前页对象', pagination)
  82. print('总数据量', pagination.total)
  83. print('当前页数据列表', pagination.items)
  84. print('总页码', pagination.pages)
  85. print()
  86. print('是否有上一页', pagination.has_prev)
  87. print('上一页页码', pagination.prev_num)
  88. print('上一页对象', pagination.prev())
  89. print('上一页对象的数据列表', pagination.prev().items)
  90. print()
  91. print('是否有下一页', pagination.has_next)
  92. print('下一页页码', pagination.next_num)
  93. print('下一页对象', pagination.next())
  94. print('下一页对象的数据列表', pagination.next().items)
  95. # """前后端分离推荐使用的 json 结果,这里没用到"""
  96. data = {
  97. "page": pagination.page, # 当前页码
  98. "pages": pagination.pages, # 总页码
  99. "has_prev": pagination.has_prev, # 是否有上一页
  100. "prev_num": pagination.prev_num, # 上一页页码
  101. "has_next": pagination.has_next, # 是否有下一页
  102. "next_num": pagination.next_num, # 下一页页码
  103. "items": [{
  104. "id": item.id,
  105. "name": item.name,
  106. "age": item.age,
  107. "sex": item.sex,
  108. "money": item.money,
  109. } for item in pagination.items]
  110. }
  111. return render_template('list.html', **locals())
  112. if __name__ == '__main__':
  113. with app.app_context():
  114. db.drop_all() # 启动时先删除相关表,后创建相关表
  115. db.create_all()
  116. app.run('0.0.0.0', 9527)

最后就是 list.html 这个模板文件,呈现一个分页的演示:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. body {
  8. font-family: Arial, sans-serif;
  9. background-color: #f4f7fa;
  10. color: #333;
  11. }
  12. table {
  13. border-collapse: collapse;
  14. margin: 50px auto;
  15. width: 80%;
  16. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  17. background-color: #fff;
  18. }
  19. th, td {
  20. padding: 12px 15px;
  21. text-align: center;
  22. }
  23. th {
  24. background-color: #007bff;
  25. color: #fff;
  26. text-transform: uppercase;
  27. }
  28. tr:nth-child(even) {
  29. background-color: #f2f2f2;
  30. }
  31. tr:hover {
  32. background-color: #e9f5ff;
  33. }
  34. .page {
  35. margin: 20px auto;
  36. text-align: center;
  37. }
  38. .page a, .page span {
  39. padding: 8px 16px;
  40. margin: 0 4px;
  41. color: #007bff;
  42. background: #fff;
  43. border: 1px solid #007bff;
  44. border-radius: 4px;
  45. text-decoration: none;
  46. transition: background-color 0.3s, color 0.3s;
  47. }
  48. .page a:hover {
  49. background-color: #007bff;
  50. color: #fff;
  51. }
  52. .page span {
  53. background-color: #007bff;
  54. color: #fff;
  55. }
  56. </style>
  57. </head>
  58. <body>
  59. <table border="1" align="center" width="600">
  60. <tr>
  61. <th>ID</th>
  62. <th>Age</th>
  63. <th>Name</th>
  64. <th>Sex</th>
  65. <th>Money</th>
  66. </tr>
  67. {% for student in pagination.items %}
  68. <tr>
  69. <td>{{ student.id }}</td>
  70. <td>{{ student.age }}</td>
  71. <td>{{ student.name }}</td>
  72. <td>{{ "男" if student.sex else "女" }}</td>
  73. <td>{{ student.money }}</td>
  74. </tr>
  75. {% endfor %}
  76. <tr align="center">
  77. <td colspan="5" class="page">
  78. {% if pagination.has_prev %}
  79. <a href="?page=1">首 页</a>
  80. <a href="?page={{ pagination.page - 1 }}">上一页</a>
  81. <a href="?page={{ pagination.page - 1 }}">{{ pagination.page - 1 }}</a>
  82. {% endif %}
  83. <span>{{ pagination.page }}</span>
  84. {% if pagination.has_next %}
  85. <a href="?page={{ pagination.page + 1 }}">{{ pagination.page + 1 }}</a>
  86. <a href="?page={{ pagination.page + 1 }}">下一页</a>
  87. <a href="?page={{ pagination.pages }}">尾 页</a>
  88. {% endif %}
  89. </td>
  90. </tr>
  91. </table>
  92. </body>
  93. </html>

为了确保能够有一定数量的数据,请你另外新建一个 request.py,用于创建大量数据(如果你知道 faker 的使用,也可以自己弄一些数据),先启动 manage.py,保证后端服务的开启和路由可用,然后直接运行该文件后可添加测试数据:

  1. # request.py
  2. import requests # pip install requests
  3. students = [ # 虚拟数据,务必当真
  4. {
  5. 'name': '王毅',
  6. 'age': 21,
  7. 'sex': 1,
  8. 'email': 'wangyi@gmail.com',
  9. 'money': 4488.5
  10. },
  11. {
  12. 'name': '张晓',
  13. 'age': 19,
  14. 'sex': 0,
  15. 'email': 'zhangxiao@example.com',
  16. 'money': 2389.75
  17. },
  18. {
  19. 'name': '李春阳',
  20. 'age': 23,
  21. 'sex': 1,
  22. 'email': 'lichunyang@outlook.com',
  23. 'money': 6715.32
  24. },
  25. {
  26. 'name': '刘瑞',
  27. 'age': 20,
  28. 'sex': 0,
  29. 'email': 'liurui@yahoo.com',
  30. 'money': 3456.89
  31. },
  32. {
  33. 'name': '陈欢',
  34. 'age': 22,
  35. 'sex': 1,
  36. 'email': 'chenhuan@gmail.com',
  37. 'money': 5678.12
  38. },
  39. {
  40. 'name': '吴娜',
  41. 'age': 18,
  42. 'sex': 0,
  43. 'email': 'wuna@example.org',
  44. 'money': 1234.56
  45. },
  46. {
  47. 'name': '赵丹',
  48. 'age': 24,
  49. 'sex': 0,
  50. 'email': 'zhaoda@outlook.com',
  51. 'money': 7890.43
  52. },
  53. {
  54. 'name': '孙宇',
  55. 'age': 21,
  56. 'sex': 1,
  57. 'email': 'sunyu@yahoo.co.jp',
  58. 'money': 4567.89
  59. },
  60. {
  61. 'name': '黄宇',
  62. 'age': 19,
  63. 'sex': 1,
  64. 'email': 'huangyu@gmail.com',
  65. 'money': 2345.67
  66. },
  67. {
  68. 'name': '杨静',
  69. 'age': 22,
  70. 'sex': 0,
  71. 'email': 'yangjing@example.com',
  72. 'money': 6789.01
  73. }
  74. ]
  75. for student in students:
  76. response = requests.request('POST', 'http://127.0.0.1:9527/students', data=student)
  77. print('添加一条记录', response.json())

确定 Flask 项目正常启动,并且上面的数据也完成了注入,如果你发现启动失败了,请检查路由、数据库连接是否有问题,你可能需要一定的 Flask 基础知识。接下来如何访问我们渲染的模板呢?

根据路由视图和设置的访问端口(9527):

  1. @app.route('/students/data', methods=['GET'])
  2. def students_data():
  3. ...
  4. return render_template('list.html', **locals())

我们直接在浏览器访问:http://127.0.0.1:9527/students/data 这个地址即可。

上述案例是演示所用,随意写的,小部分代码参考了某机构的教程代码示例,平台原因无法标注,路由设计也是很随便的,这种代码如果存在版权纠纷,emmm.....,请联系我删除,谢谢。无私开源,只为搞懂后端开发的学习,请勿钻牛角……

原文链接:https://www.cnblogs.com/gupingan/p/18300467

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号