Skip to content

Commit ee8134d

Browse files
committed
feat(pagination): replace with database level limit
1 parent 28a7c7d commit ee8134d

File tree

6 files changed

+28
-48
lines changed

6 files changed

+28
-48
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ flask run
4141

4242
学生、教师自动选择合适的登录方式,并保存
4343

44-
在数据库层面使用 limit 和 offset 分页
45-
4644
基于 Go 重写
4745

4846
## 提示

src/ZhiXueLite/app/admin/routes.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from sqlalchemy import select
66
from app.database import db
77
from app.database.models import Exam, School, ZhiXueStudentAccount, User
8-
from app.utils.paginate import paginated_json
8+
from app.utils.paginate import paginate_query
99
from loguru import logger
1010

1111
admin_bp = Blueprint("admin", __name__)
@@ -31,12 +31,10 @@ def list_schools():
3131
per_page = request.args.get("per_page", 10, type=int)
3232
query = request.args.get("query", "", type=str)
3333

34-
stmt = select(School)
34+
stmt = select(School).order_by(School.id.asc())
3535
if query:
3636
stmt = stmt.where(School.name.contains(query) | School.id.contains(query))
37-
38-
schools = db.session.scalars(stmt).all()
39-
paginated_schools = paginated_json(schools, page, per_page)
37+
paginated_schools = paginate_query(stmt, page, per_page)
4038
school_list = [{"id": school.id, "name": school.name} for school in paginated_schools["items"]]
4139

4240
return jsonify({
@@ -56,9 +54,7 @@ def list_users():
5654
stmt = select(User).order_by(User.id.asc())
5755
if query:
5856
stmt = stmt.where(User.username.contains(query))
59-
60-
users = db.session.scalars(stmt).all()
61-
paginated_users = paginated_json(users, page, per_page)
57+
paginated_users = paginate_query(stmt, page, per_page)
6258
user_list = [user.to_dict_all() for user in paginated_users["items"]]
6359

6460
return jsonify({
@@ -75,12 +71,10 @@ def list_zhixue_accounts():
7571
per_page = request.args.get("per_page", 10, type=int)
7672
query = request.args.get("query", "", type=str)
7773

78-
stmt = select(ZhiXueStudentAccount)
74+
stmt = select(ZhiXueStudentAccount).order_by(ZhiXueStudentAccount.id.asc())
7975
if query:
8076
stmt = stmt.where(ZhiXueStudentAccount.username.contains(query))
81-
82-
zhixue_accounts = db.session.scalars(stmt).all()
83-
paginated_accounts = paginated_json(zhixue_accounts, page, per_page)
77+
paginated_accounts = paginate_query(stmt, page, per_page)
8478
account_list = [account.to_dict_all() for account in paginated_accounts["items"]]
8579

8680
return jsonify({
@@ -97,12 +91,10 @@ def list_exams():
9791
per_page = request.args.get("per_page", 10, type=int)
9892
query = request.args.get("query", "", type=str)
9993

100-
stmt = select(Exam).order_by(Exam.created_at.desc())
94+
stmt = select(Exam).order_by(Exam.created_at.desc(), Exam.id.desc())
10195
if query:
10296
stmt = stmt.where(Exam.name.contains(query) | Exam.id.contains(query))
103-
104-
exams = db.session.scalars(stmt).all()
105-
paginated_exams = paginated_json(exams, page, per_page)
97+
paginated_exams = paginate_query(stmt, page, per_page)
10698
exam_list = [
10799
{
108100
"id": exam.id,

src/ZhiXueLite/app/exam/routes.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import os
66
import time
77
from openpyxl import Workbook
8-
from sqlalchemy import func, literal, select, desc, union_all
8+
from sqlalchemy import func, select
99
from app.database import db
1010
from app.database.models import Exam, ExamSchool, PermissionLevel, Score, User, UserExam, ZhiXueTeacherAccount, PermissionType
1111
from app.models.exceptions import FailedToGetTeacherAccountError
1212
from app.models.teacher import login_teacher_session
1313
from app.task.repository import create_task
14-
from app.utils.paginate import paginated_json
14+
from app.utils.paginate import paginate_query
1515
from app import limiter
1616
from flask_limiter.util import get_remote_address
1717

@@ -133,9 +133,8 @@ def get_exam_list():
133133
elif not (start_time == 0 and end_time == 0):
134134
return jsonify({"success": False, "message": "参数不合法"}), 400
135135

136-
exams = db.session.scalars(stmt.order_by(desc(Exam.created_at))).all()
137-
138-
paginated_exams = paginated_json(exams, page, per_page)
136+
stmt = stmt.order_by(Exam.created_at.desc(), Exam.id.desc())
137+
paginated_exams = paginate_query(stmt, page, per_page)
139138
exam_list = []
140139

141140
has_global_permission = current_user.has_permission(

src/ZhiXueLite/app/task/routes.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from flask import Blueprint, request, jsonify
22
from flask_login import login_required, current_user
3-
from sqlalchemy import select, desc
3+
from sqlalchemy import select
44
from app.database import db
55
from app.database.models import BackgroundTask, TaskStatus
6-
from app.utils.paginate import paginated_json
6+
from app.utils.paginate import paginate_query
77
from . import repository as task_repo
88

99

@@ -49,9 +49,8 @@ def get_user_tasks():
4949
except ValueError:
5050
return jsonify({"success": False, "message": "无效的状态值"}), 400
5151

52-
tasks = db.session.scalars(stmt.order_by(desc(BackgroundTask.created_at))).all()
53-
54-
paginated_tasks = paginated_json(tasks, page, per_page)
52+
stmt = stmt.order_by(BackgroundTask.created_at.desc(), BackgroundTask.id.desc())
53+
paginated_tasks = paginate_query(stmt, page, per_page)
5554
task_list = [task.to_dict() for task in paginated_tasks["items"]]
5655

5756
return jsonify({

src/ZhiXueLite/app/teacher/routes.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
from functools import wraps
21
from flask import Blueprint, jsonify, request
32
from flask_login import current_user, login_required
4-
from loguru import logger
53
from sqlalchemy import select
64
from app.database import db
75
from app.database.models import School, ZhiXueTeacherAccount
86
from app.models.teacher import login_teacher
9-
from app.utils.paginate import paginated_json
7+
from app.utils.paginate import paginate_query
108

119
teacher_bp = Blueprint("teacher", __name__)
1210

@@ -33,9 +31,9 @@ def get_teacher_list():
3331
stmt = stmt.where(ZhiXueTeacherAccount.username.contains(query) |
3432
School.name.contains(query))
3533

36-
teachers = db.session.scalars(stmt).all()
34+
stmt = stmt.order_by(ZhiXueTeacherAccount.id.asc())
3735

38-
paginated_teachers = paginated_json(teachers, page, per_page)
36+
paginated_teachers = paginate_query(stmt, page, per_page)
3937

4038
teacher_list = [{
4139
"id": teacher.id,
Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
1-
def paginate(items, page, per_page):
2-
if not isinstance(items, list):
3-
raise ValueError("Items must be a list.")
1+
from sqlalchemy import func, select
2+
from app.database import db
43

5-
if page < 1 or per_page < 1:
6-
raise ValueError("Page and per_page must be greater than 0.")
74

8-
start = (page - 1) * per_page
9-
end = start + per_page
10-
paginated_items = items[start:end]
5+
def paginate_query(stmt, page, per_page):
6+
"""数据库级别分页,stmt 应已包含 where/order_by 条件"""
7+
total = db.session.scalar(select(func.count()).select_from(stmt.subquery()))
118

12-
return paginated_items, len(items), end
9+
stmt = stmt.limit(per_page).offset((page - 1) * per_page)
10+
items = db.session.scalars(stmt).all()
1311

14-
15-
def paginated_json(items, page, per_page):
16-
"""将数据转换为分页的 JSON 格式"""
17-
paginated_items, total, end = paginate(items, page, per_page)
1812
return {
19-
"items": paginated_items,
13+
"items": items,
2014
"pagination": {
2115
"page": page,
2216
"per_page": per_page,
2317
"total": total,
2418
"pages": (total + per_page - 1) // per_page,
2519
"has_prev": page > 1,
26-
"has_next": end < total
20+
"has_next": page * per_page < total
2721
},
2822
}

0 commit comments

Comments
 (0)