分页查询及其拓展应用案例
分页查询
分页查询是处理大量数据时常用的技术,通过分页可以将数据分成多个小部分,方便用户逐页查看。SQLAlchemy 提供了简单易用的方法来实现分页查询。
本篇我们也会在最终实现这样的分页效果:
1. 什么是分页查询
分页查询是将查询结果按照一定数量分成多页展示,每页显示固定数量的记录。分页查询通常使用两个参数:
-
limit
:每页显示的记录数量。 -
offset
:跳过的记录数量。
例如,要查询第二页,每页显示 10 条记录:
-
limit
:10 -
offset
:10
2. 使用 SQLAlchemy 实现分页查询
基本查询
首先,我们需要一个基本的查询来获取数据:
import db
from model import Studentdef basic_query():students = db.session.query(Student).all()for stu in students:print(stu.to_dict())
使用 limit
和 offset
前文中,我们已经了解到 SQLAlchemy 提供了 limit
和 offset
方法来实现分页查询。limit
限制返回的记录数量,offset
跳过指定数量的记录。
import db
from model import Studentdef paginated_query(page, per_page):q = db.select(Student).limit(per_page).offset((page - 1) * per_page)students = db.session.execute(q).scalars()for stu in students:print(stu.to_dict())
例如,要获取第 2 页,每页显示 10 条记录:
paginated_query(2, 10)
对应的 SQL 语句:
SELECT * FROM tb_student LIMIT 10 OFFSET 10;
3. 前后端实现分页功能
后端分页
在后端实现分页功能时,可以创建一个函数来处理分页逻辑。这个函数接受 page
和 per_page
参数,并返回当前页的数据和总页数。
import db
from model import Studentdef get_paginated_students(page, per_page):total = db.session.query(Student).count()q = db.select(Student).limit(per_page).offset((page - 1) * per_page)students = db.session.execute(q).scalars()return {'total': total,'page': page,'per_page': per_page,'pages': (total + per_page - 1) // per_page,'data': [stu.to_dict() for stu in students]}
前端分页
在前端实现分页时,可以使用后端提供的分页数据来渲染页面:
{"total": 100,"page": 2,"per_page": 10,"pages": 10,"data": [{"id": 11, "name": "Student 11", ...},{"id": 12, "name": "Student 12", ...},...]
}
前端可以根据这些数据渲染分页控件和当前页的数据。
[拓展] Flask 分页演示
下面是一个前后端不分离的 Flask 项目,代码文件比较多,你需要自行理一下。同时也要保证 Flask
、 Flask-SQLAlchemy
与 Flask-MysqlDB
的安装。
pip install flask
pip install flask-sqlalchemy # 兼容 Flask 的 SQLAlchemy 框架,提供 ORM 功能
pip install flask-mysqldb # 为 Flask-SQLAlchemy 提供 MySQL 驱动
Flask 项目目录如下:
flask_app/ # 项目目录
├── templates/ # 模板目录
│ └── list.html # 模板文件
├── config.py # Flask 配置文件
├── db.py # 数据库核心文件,包含重要操作
├── manage.py # Flask 路由和业务视图文件
└── models.py # 数据库模型文件
首先看一下配置文件 config.py
:
class Config:SQLALCHEMY_DATABASE_URI = 'mysql://root:0908@localhost:3306/db_flask_demo_school?charset=utf8mb4' # 数据库连接。自行替换数据库用户名称和密码以及实际数据库名SQLALCHEMY_ECHO = False # 是否打印执行的 SQL 语句及其耗时DEBUG = True # 是否启用调试模式
db.py
"""
Create database:> create database db_flask_demo_school charset=utf8mb4
"""
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import *db = SQLAlchemy()
models.py
from db import *class Student(db.Model):__tablename__ = 'tb_student2'id = db.Column(db.Integer, primary_key=True, comment="主键")name = db.Column(db.String(15), index=True, comment="姓名")age = db.Column(db.SmallInteger, comment="年龄")sex = db.Column(db.Boolean, comment="性别")email = db.Column(db.String(128), unique=True, comment="邮箱地址")money = db.Column(db.Numeric(10, 2), default=0.0, comment="钱包")def to_dict(self):return {'id': self.id,'name': self.name,'age': self.age,'sex': self.sex,'email': self.email,'money': float(self.money)}def __repr__(self):return f'<{self.__class__.__name__}: {self.name}>'
然后就是 manage.py
,编写了路由与业务代码:
from pathlib import Path
from flask import Flask, jsonify, request, render_template
from config import Config
from models import db, Studentapp = Flask(__name__, template_folder='./templates')
app.config.from_object(Config)db.init_app(app)@app.route('/', methods=['GET'])
def index():"""没啥用,勿看"""title = Path(__file__).namereturn title@app.route('/students', methods=['POST'])
def create_student():"""采集访问的信息,创建学生"""sex = request.form.get('sex')sex = int(sex) if sex.isdigit() else 0student = Student(name=request.form.get('name', '未知'),age=request.form.get('age', 0),sex=bool(sex),email=request.form.get('email', ''),money=request.form.get('money', 0),)if request.form.get('id', None) is not None:student.id = request.form['id']db.session.add(student)db.session.commit()return jsonify({'success': True,'data': student.to_dict(),'msg': 'success'}), 201@app.route('/students', methods=['DELETE'])
def delete_students():"""删除学生表的所有记录"""db.session.execute(db.delete(Student))db.session.commit()return jsonify({'success': True,'data': None,'msg': 'success'})@app.route('/students', methods=['GET'])
def get_students():# 旧版本 2.x 获取全部数据# students = Student.query.all()# 新版本 3.1.x 获取全部数据students = db.session.execute(db.select(Student).where()).scalars()return jsonify({'success': True,'data': {'count': Student.query.count(),'students': [student.to_dict() for student in students]},'msg': 'success'})@app.route('/students/<int:student_id>', methods=['GET'])
def get_student(student_id):# 根据主键查询数据,不存在则为 Nonestudent = db.session.get(Student, student_id)if not student:return jsonify({'success': False,'data': None,'msg': 'student not found'})return jsonify({'success': True,'data': student.to_dict(),'msg': 'success'})@app.route('/students/data', methods=['GET'])
def students_data():"""这里是分页器的使用,不同于我们所使用的 limit 和 offset 需要自己编写"""# 不采取数据分页时,大量数据时会导致服务器运存膨胀,这是非常不妥的page = request.args.get('page', 1, type=int)per_page = request.args.get('size', 3, type=int)# 创建分页器对象pagination = Student.query.paginate(page=page, per_page=per_page, max_per_page=20)print('当前页对象', pagination)print('总数据量', pagination.total)print('当前页数据列表', pagination.items)print('总页码', pagination.pages)print()print('是否有上一页', pagination.has_prev)print('上一页页码', pagination.prev_num)print('上一页对象', pagination.prev())print('上一页对象的数据列表', pagination.prev().items)print()print('是否有下一页', pagination.has_next)print('下一页页码', pagination.next_num)print('下一页对象', pagination.next())print('下一页对象的数据列表', pagination.next().items)# """前后端分离推荐使用的 json 结果,这里没用到"""data = {"page": pagination.page, # 当前页码"pages": pagination.pages, # 总页码"has_prev": pagination.has_prev, # 是否有上一页"prev_num": pagination.prev_num, # 上一页页码"has_next": pagination.has_next, # 是否有下一页"next_num": pagination.next_num, # 下一页页码"items": [{"id": item.id,"name": item.name,"age": item.age,"sex": item.sex,"money": item.money,} for item in pagination.items]}return render_template('list.html', **locals())if __name__ == '__main__':with app.app_context():db.drop_all() # 启动时先删除相关表,后创建相关表db.create_all()app.run('0.0.0.0', 9527)
最后就是 list.html
这个模板文件,呈现一个分页的演示:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>body {font-family: Arial, sans-serif;background-color: #f4f7fa;color: #333;}table {border-collapse: collapse;margin: 50px auto;width: 80%;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);background-color: #fff;}th, td {padding: 12px 15px;text-align: center;}th {background-color: #007bff;color: #fff;text-transform: uppercase;}tr:nth-child(even) {background-color: #f2f2f2;}tr:hover {background-color: #e9f5ff;}.page {margin: 20px auto;text-align: center;}.page a, .page span {padding: 8px 16px;margin: 0 4px;color: #007bff;background: #fff;border: 1px solid #007bff;border-radius: 4px;text-decoration: none;transition: background-color 0.3s, color 0.3s;}.page a:hover {background-color: #007bff;color: #fff;}.page span {background-color: #007bff;color: #fff;}</style>
</head>
<body><table border="1" align="center" width="600"><tr><th>ID</th><th>Age</th><th>Name</th><th>Sex</th><th>Money</th></tr>{% for student in pagination.items %}<tr><td>{{ student.id }}</td><td>{{ student.age }}</td><td>{{ student.name }}</td><td>{{ "男" if student.sex else "女" }}</td><td>{{ student.money }}</td></tr>{% endfor %}<tr align="center"><td colspan="5" class="page">{% if pagination.has_prev %}<a href="?page=1">首 页</a><a href="?page={{ pagination.page - 1 }}">上一页</a><a href="?page={{ pagination.page - 1 }}">{{ pagination.page - 1 }}</a>{% endif %}<span>{{ pagination.page }}</span>{% if pagination.has_next %}<a href="?page={{ pagination.page + 1 }}">{{ pagination.page + 1 }}</a><a href="?page={{ pagination.page + 1 }}">下一页</a><a href="?page={{ pagination.pages }}">尾 页</a>{% endif %}</td></tr></table>
</body>
</html>
为了确保能够有一定数量的数据,请你另外新建一个 request.py
,用于创建大量数据(如果你知道 faker 的使用,也可以自己弄一些数据),先启动 manage.py
,保证后端服务的开启和路由可用,然后直接运行该文件后可添加测试数据:
# request.py
import requests # pip install requestsstudents = [ # 虚拟数据,务必当真{'name': '王毅','age': 21,'sex': 1,'email': 'wangyi@gmail.com','money': 4488.5},{'name': '张晓','age': 19,'sex': 0,'email': 'zhangxiao@example.com','money': 2389.75},{'name': '李春阳','age': 23,'sex': 1,'email': 'lichunyang@outlook.com','money': 6715.32},{'name': '刘瑞','age': 20,'sex': 0,'email': 'liurui@yahoo.com','money': 3456.89},{'name': '陈欢','age': 22,'sex': 1,'email': 'chenhuan@gmail.com','money': 5678.12},{'name': '吴娜','age': 18,'sex': 0,'email': 'wuna@example.org','money': 1234.56},{'name': '赵丹','age': 24,'sex': 0,'email': 'zhaoda@outlook.com','money': 7890.43},{'name': '孙宇','age': 21,'sex': 1,'email': 'sunyu@yahoo.co.jp','money': 4567.89},{'name': '黄宇','age': 19,'sex': 1,'email': 'huangyu@gmail.com','money': 2345.67},{'name': '杨静','age': 22,'sex': 0,'email': 'yangjing@example.com','money': 6789.01}
]
for student in students:response = requests.request('POST', 'http://127.0.0.1:9527/students', data=student)print('添加一条记录', response.json())
确定 Flask 项目正常启动,并且上面的数据也完成了注入,如果你发现启动失败了,请检查路由、数据库连接是否有问题,你可能需要一定的 Flask 基础知识。接下来如何访问我们渲染的模板呢?
根据路由视图和设置的访问端口(9527):
@app.route('/students/data', methods=['GET'])
def students_data():...return render_template('list.html', **locals())
我们直接在浏览器访问:http://127.0.0.1:9527/students/data 这个地址即可。
上述案例是演示所用,随意写的,小部分代码参考了某机构的教程代码示例,平台原因无法标注,路由设计也是很随便的,这种代码如果存在版权纠纷,emmm.....,请联系我删除,谢谢。无私开源,只为搞懂后端开发的学习,请勿钻牛角……
文章转载自:顾平安
原文链接:https://www.cnblogs.com/gupingan/p/18300467
体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构
相关文章:

分页查询及其拓展应用案例
分页查询 分页查询是处理大量数据时常用的技术,通过分页可以将数据分成多个小部分,方便用户逐页查看。SQLAlchemy 提供了简单易用的方法来实现分页查询。 本篇我们也会在最终实现这样的分页效果: 1. 什么是分页查询 分页查询是将查询结果按照…...

【UE5.1】NPC人工智能——02 NPC移动到指定位置
效果 步骤 1. 新建一个蓝图,父类选择“AI控制器” 这里命名为“BP_NPC_AIController”,表示专门用于控制NPC的AI控制器 2. 找到我们之前创建的所有NPC的父类“BP_NPC” 打开“BP_NPC”,在类默认值中,将“AI控制器类”一项设置为“…...

有关电力电子技术的一些相关仿真和分析:⑤交-直-交全桥逆变+全波整流结构电路(MATLAB/Siumlink仿真)
全桥逆变+全波整流结构 参数:Vin=500V, Vo=200V, T=2:1:1, RL=10Ω, fs=100kHz, L=1mH, C=100uF (1)给定输入电压,输出电压和主电路参数,仿真研究电路工作原理,分析工作时序; (2)调节负载电阻,实现电流连续和断续,并仿真验证; (3)调节占空比,分析占空比与电…...
记录一次Android推流、录像踩坑过程
背景: 按照需求,需要支持APP在手机息屏时进行推流、录像。 技术要点: 1、手机在息屏时能够打开camera获取预览数据 2、获取预览数据时进行编码以及合成视频 一、息屏时获取camera预览数据: ①Camera.setPreviewDisplay(SurfaceH…...

VsCode 与远程服务器 ssh免密登录
首先配置信息 加入下列信息 Host qb-zn HostName 8.1xxx.2xx.3xx User root ForwardAgent yes Port 22 IdentityFile ~/.ssh/id_rsa 找到自己的公钥,不带pub是私钥,打死都不能给别人。复制公钥 拿到公钥后,来到远程服务器 vim ~/.ss…...

7/13 - 7/15
vo.setId(rs.getLong("id"))什么意思? vo.setId(rs.getLong("id")); 这行代码是在Java中使用ResultSet对象(通常用于从数据库中检索数据)获取一个名为"id"的列,并将其作为long类型设置为一个对象…...

烟雾监测与太阳能源:实验装置在其中的作用
太阳光在烟雾中的散射效应研究实验装置是一款模拟阳光透过烟雾环境的设备。此装置能帮助探究阳光在烟雾中的传播特性、散射特性及其对阳光的影响。 该装置主要包括光源单元、烟雾发生装置、光学组件、以及系统。光源单元负责产生类似于太阳光的光线,通常选用高亮度的…...
QT下,如何获取控制台输入
最近工作中为了测试某个模块,需要把原先输入模块部分,改成控制台输入来方便测试。在QT中,我们可以使用 QTextStream 类来读取用户的输入来达到目的。下面是一个简单的例子: #include <QCoreApplication> #include <QTex…...

mybatis动态传入参数 pgsql 日期 Interval ,day,minute
mybatis动态传入参数 pgsql 日期 Interval 在navicat中,标准写法 SELECT * FROM test WHERE time > (NOW() - INTERVAL 5 day)在mybatis中,错误写法 SELECT * FROM test WHERE time > (NOW() - INTERVAL#{numbers,jdbcTypeINTEGER} day)报错内…...
常见CSS属性
常见CSS属性。 1. display: 定义:display 属性控制元素如何渲染在文档流中,影响了元素是否占用空间、位置及盒子模型的行为。 使用说明:它可以设置为如block, inline, inline-block, flex, grid, none等值,用于决定元素显示模式…...

WSL-Ubuntu20.04训练环境配置
1.YOLOv8训练环境配置 训练环境配置的话就仍然以YOLOv8为例,来说明如何配置深度学习训练环境。这部分内容比较简单,主要是安装miniAnaconda以及安装torch和torchvision. 首先是miniAnaconda的安装(参考官网的教程Miniconda — Anaconda ),执行…...
运维检查:mysql表自增id是否快要用完
数据库表中最大自增ID用完会报错。判断是否接近或达到自增ID类型的最大值: 对于MySQL中的自增ID,如果使用的是int类型,其无符号(unsigned)的最大值可以达到2^32 - 1,即4294967295。如果使用的…...

深入理解FFmpeg--libavformat接口使用(一)
libavformat(lavf)是一个用于处理各种媒体容器格式的库。它的主要两个目的是去复用(即将媒体文件拆分为组件流)和复用的反向过程(以指定的容器格式写入提供的数据)。它还有一个I/O模块,支持多种…...
坚持日更的意义何在?
概述 日更,就是每天更新一次或一篇文章。 坚持日更,就是坚持每天更新一次或一篇文章。 这里用了坚持,实际上不是恰当的表述,正确的感觉应该是让日更当作习惯,然后,让自己习惯每天去更新一篇文章。 日更…...

内容长度不同的div如何自动对齐展示
平时我们经常会遇到页面内容div结构相同页,这时为了美观我们会希望div会对齐展示,但当div里的文字长度不一时又不想写固定高度,就会出现div长度长长短短,此时实现样式可以这样写: .e-commerce-Wrap {display: flex;fle…...

Qt中https的使用,报错TLS initialization failed和不能打开ssl.lib问题解决
前言 在现代应用程序中,安全地传输数据变得越来越重要。Qt提供了一套完整的网络API来支持HTTP和HTTPS通信。然而,在实际开发过程中,开发者可能会遇到SSL相关的错误,例如“TLS initialization failed”,cantt open ssl…...

P2p网络性能测度及监测系统模型
P2p网络性能测度及监测系统模型 网络IP性能参数 IP包传输时延时延变化误差率丢失率虚假率吞吐量可用性连接性测度单向延迟测度单向分组丢失测度往返延迟测度 OSI中的位置-> 网络层 用途 面相业务的网络分布式计算网络游戏IP软件电话流媒体分发多媒体通信 业务质量 通过…...
zookeeper相关总结
1. ZooKeeper 的架构 ZooKeeper 采用主从架构(Leader-Follower 模型),包括以下组件: Leader:负责处理所有写请求和协调事务一致性。Follower:处理读请求并转发写请求给 Leader。参与 Leader 选举和事务提…...

【openwrt】Openwrt系统新增普通用户指南
文章目录 1 如何新增普通用户2 如何以普通用户权限运行服务3 普通用户如何访问root账户的ubus服务4 其他权限控制5 参考 Openwrt系统在默认情况下只提供一个 root账户,所有的服务都是以 root权限运行的,包括 WebUI也是通过root账户访问的,…...

【GD32】从零开始学GD32单片机 | WDGT看门狗定时器+独立看门狗和窗口看门狗例程(GD32F470ZGT6)
1. 简介 看门狗从本质上来说也是一个定时器,它是用来监测硬件或软件的故障的;它的工作原理大概就是开启后内部定时器会按照设置的频率更新,在程序运行过程中我们需不断地重装载看门狗,以使它不溢出;如果硬件或软件发生…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...