FastAPI 与 SQLModel 分页功能实现指南

news2024/12/26 15:15:27

FastAPI 与 SQLModel 分页功能实现指南

1. 基础分页模型

from typing import Generic, TypeVar, Optional, List
from pydantic import BaseModel
from sqlmodel import SQLModel

T = TypeVar("T")

class PageParams(BaseModel):
    page: int = 1
    size: int = 10
    
class PageResponse(SQLModel, Generic[T]):
    items: List[T]
    total: int
    page: int
    size: int
    pages: int
    has_next: bool
    has_prev: bool
    
    @property
    def next_page(self) -> Optional[int]:
        if self.has_next:
            return self.page + 1
        return None
    
    @property
    def prev_page(self) -> Optional[int]:
        if self.has_prev:
            return self.page - 1
        return None

2. 通用分页函数

2.1 基础分页查询

from sqlmodel import select, Session, func

def paginate_query(
    session: Session,
    query,
    page: int = 1,
    size: int = 10
) -> PageResponse:
    # 计算总数
    total = session.exec(select(func.count()).select_from(query.subquery())).one()
    
    # 计算总页数
    pages = (total + size - 1) // size
    
    # 应用分页
    items = session.exec(
        query.offset((page - 1) * size).limit(size)
    ).all()
    
    return PageResponse(
        items=items,
        total=total,
        page=page,
        size=size,
        pages=pages,
        has_next=page < pages,
        has_prev=page > 1
    )

2.2 带过滤和排序的分页查询

from typing import Optional, List
from sqlalchemy import desc, asc

def get_users_paginated(
    session: Session,
    page: int = 1,
    size: int = 10,
    search: Optional[str] = None,
    sort_by: Optional[str] = None,
    sort_order: str = "asc"
) -> PageResponse[User]:
    query = select(User)
    
    # 添加搜索条件
    if search:
        query = query.where(
            User.username.ilike(f"%{search}%") |
            User.email.ilike(f"%{search}%")
        )
    
    # 添加排序
    if sort_by:
        direction = asc if sort_order == "asc" else desc
        query = query.order_by(direction(getattr(User, sort_by)))
    
    return paginate_query(session, query, page, size)

3. FastAPI 路由实现

3.1 基础分页路由

from fastapi import APIRouter, Depends, Query
from sqlmodel import Session

router = APIRouter()

@router.get("/users", response_model=PageResponse[User])
def get_users(
    page: int = Query(1, ge=1),
    size: int = Query(10, ge=1, le=100),
    session: Session = Depends(get_session)
):
    query = select(User)
    return paginate_query(session, query, page, size)

3.2 高级分页路由

@router.get("/users/advanced", response_model=PageResponse[User])
def get_users_advanced(
    page: int = Query(1, ge=1),
    size: int = Query(10, ge=1, le=100),
    search: Optional[str] = Query(None),
    sort_by: Optional[str] = Query(None),
    sort_order: str = Query("asc", regex="^(asc|desc)$"),
    session: Session = Depends(get_session)
):
    return get_users_paginated(
        session=session,
        page=page,
        size=size,
        search=search,
        sort_by=sort_by,
        sort_order=sort_order
    )

4. 前端分页实现

4.1 使用 Axios 调用分页 API

interface PaginationParams {
  page: number;
  size: number;
  search?: string;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

async function fetchUsers(params: PaginationParams) {
  const { page, size, search, sortBy, sortOrder } = params;
  const response = await axios.get('/api/users/advanced', {
    params: {
      page,
      size,
      search,
      sort_by: sortBy,
      sort_order: sortOrder
    }
  });
  return response.data;
}

4.2 Vue 3 分页组件

<template>
  <div class="pagination">
    <!-- 数据展示 -->
    <div class="items">
      <div v-for="item in pageData.items" :key="item.id">
        {{ item.username }}
      </div>
    </div>
    
    <!-- 分页控制 -->
    <div class="controls">
      <button 
        :disabled="!pageData.has_prev"
        @click="changePage(pageData.prev_page)"
      >
        上一页
      </button>
      
      <span>{{ pageData.page }} / {{ pageData.pages }}</span>
      
      <button 
        :disabled="!pageData.has_next"
        @click="changePage(pageData.next_page)"
      >
        下一页
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const pageData = ref({
  items: [],
  total: 0,
  page: 1,
  size: 10,
  pages: 0,
  has_next: false,
  has_prev: false
});

const loading = ref(false);

async function loadData(page = 1) {
  loading.value = true;
  try {
    const data = await fetchUsers({
      page,
      size: 10
    });
    pageData.value = data;
  } catch (error) {
    console.error('Failed to load data:', error);
  } finally {
    loading.value = false;
  }
}

function changePage(newPage: number) {
  if (newPage) {
    loadData(newPage);
  }
}

onMounted(() => {
  loadData();
});
</script>

<style scoped>
.pagination {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.controls {
  display: flex;
  justify-content: center;
  gap: 1rem;
  align-items: center;
}

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>

5. 性能优化建议

  1. 查询优化
    • 为分页字段添加索引
    • 使用 COUNT(*) OVER() 优化计数查询
    • 对大数据集使用游标分页
def optimize_count_query(session: Session, query):
    # 使用窗口函数优化计数
    count_query = query.add_columns(func.count().over().label("total"))
    first_result = session.exec(count_query).first()
    return first_result.total if first_result else 0
  1. 缓存策略
from functools import lru_cache

@lru_cache(maxsize=100)
def get_cached_page(page: int, size: int, cache_key: str):
    return get_users_paginated(page=page, size=size)

6. 常见问题解决

6.1 处理空结果

def handle_empty_page(
    session: Session,
    query,
    page: int,
    size: int
) -> PageResponse:
    total = session.exec(select(func.count()).select_from(query.subquery())).one()
    
    if total == 0:
        return PageResponse(
            items=[],
            total=0,
            page=page,
            size=size,
            pages=0,
            has_next=False,
            has_prev=False
        )
    
    # 如果请求的页码超出范围,返回最后一页
    pages = (total + size - 1) // size
    if page > pages:
        return paginate_query(session, query, pages, size)
    
    return paginate_query(session, query, page, size)

6.2 处理无效参数

def validate_page_params(page: int, size: int) -> tuple[int, int]:
    """验证并修正分页参数"""
    page = max(1, page)  # 页码最小为1
    size = max(1, min(size, 100))  # 每页大小在1-100之间
    return page, size

7. 测试分页功能

def test_pagination():
    # 创建测试数据
    users = [User(username=f"user{i}", email=f"user{i}@example.com") 
             for i in range(25)]
    
    with Session(engine) as session:
        session.add_all(users)
        session.commit()
        
        # 测试基本分页
        page1 = paginate_query(session, select(User), page=1, size=10)
        assert len(page1.items) == 10
        assert page1.total == 25
        assert page1.has_next is True
        
        # 测试最后一页
        page3 = paginate_query(session, select(User), page=3, size=10)
        assert len(page3.items) == 5
        assert page3.has_next is False

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2265899.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux高级--2.4.5 靠协议头保证传输的 MAC/IP/TCP/UDP---协议帧格式

任何网络协议&#xff0c;都必须要用包头里面设置写特殊字段来标识自己&#xff0c;传输越复杂&#xff0c;越稳定&#xff0c;越高性能的协议&#xff0c;包头越复杂。我们理解这些包头中每个字段的作用要站在它们解决什么问题的角度来理解。因为没人愿意让包头那么复杂。 本…

python12-变量的作用域

一、变量的作用域 1-1、全局变量-global关键字 1、修改一个全局变量 当你需要在函数内部修改全局变量的值时&#xff0c;你需要使用 global 关键字来指明这一点。如果不这样做&#xff0c;Python会认为你在函数内部创建了一个新的局部变量&#xff0c;它与全局变量同名但实际…

4X4规模S盒分量布尔函数计算工具(附各大常见分组加密算法S盒查找表和其对应分量布尔函数截图)

文章结尾有S盒分量布尔函数计算工具下载地址 Serpent {0x3,0x8,0xF,0x1,0xA,0x6,0x5,0xB,0xE,0xD,0x4,0x2,0x7,0x0,0x9,0xC} LBlock {0xE,0x9,0xF,0x0,0xD,0x4,0xA,0xB,0x1,0x2,0x8,0x3,0x7,0x6,0xC,0x5} GOST {0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0…

硬件设计-传输线匹配

目录 简介&#xff1a; 主题&#xff1a; 终端匹配 始端匹配 始端匹配的阻值 始端匹配的输出驱动电流 中间匹配 电阻阻值的选择 简介&#xff1a; 系统何时需要匹配电阻&#xff1f;按照第四章的内容来看有两种情况&#xff1a;长线传输造成信号反射的情况和短线传输造成…

Hive其十,优化和数据倾斜

目录 Hive优化 1、开启本地模式 2、explain分析SQL语句 3、修改Fetch操作 4、开启hive的严格模式【提高了安全性】 5、JVM重用 6、分区、分桶以及压缩 7、合理设置map和reduce的数量 合理设置map数量&#xff1a; 设置合理的reducer的个数 8、设置并行执行 9、CBO优…

C# 中的记录类型简介 【代码之美系列】

&#x1f380;&#x1f380;&#x1f380;代码之美系列目录&#x1f380;&#x1f380;&#x1f380; 一、C# 命名规则规范 二、C# 代码约定规范 三、C# 参数类型约束 四、浅析 B/S 应用程序体系结构原则 五、浅析 C# Async 和 Await 六、浅析 ASP.NET Core SignalR 双工通信 …

02-18.python入门基础一基础算法

&#xff08;一&#xff09;排序算法 简述&#xff1a; 在 Python 中&#xff0c;有多种常用的排序算法&#xff0c;下面为你详细介绍几种常见的排序算法及其原理、实现代码、时间复杂度以及稳定性等特点&#xff0c;并对比它们适用的场景。 冒泡排序&#xff08;Bubble Sor…

在福昕(pdf)阅读器中导航到上次阅读页面的方法

文章目录 在福昕(pdf)阅读器中导航到上次阅读页面的方法概述笔记用书签的方法来导航用导航按钮的方法来导航 备注END 在福昕(pdf)阅读器中导航到上次阅读页面的方法 概述 喜欢用福昕(pdf)阅读器来看pdf文件。 但是有个小问题困扰了我好久。 e.g. 300页的pdf看了一半&#xff…

单元测试/系统测试/集成测试知识总结

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、单元测试的概念 单元测试是对软件基本组成单元进行的测试&#xff0c;如函数或一个类的方法。当然这里的基本单元不仅仅指的是一个函数或者方法&#xff0…

光谱相机的工作原理

光谱相机的工作原理主要基于不同物质对不同波长光的吸收、反射和透射特性存在差异&#xff0c;以下是其具体工作过程&#xff1a; 一、光的收集 目标物体在光源照射下&#xff0c;其表面会对光产生吸收、反射和透射等相互作用。光谱相机的光学系统&#xff08;如透镜、反射镜…

云手机与Temu矩阵:跨境电商运营新引擎

云手机与 Temu 矩阵结合的基础 云手机技术原理 云手机基于先进的 ARM 虚拟化技术&#xff0c;在服务器端运行 APP。通过在服务器上利用容器虚拟化软件技术&#xff0c;能够虚拟出多个独立的手机操作系统实例&#xff0c;每个实例等同于一部单独的手机&#xff0c;可独立运行各…

GIT与github的链接(同步本地与远程仓库)

1.官网下载GIT Git - 安装 Git 2.GIT生成密钥 2.1 打开gitbash配置邮箱与用户名&#xff08;非初次使用GIT跳过这一步&#xff09; git config --global user.name "你的用户名" git config --global user.email "你的邮箱" 2.2 生成ssh密匙 1&#xff0…

SpeedTree For UE5学习笔记

SpeedTree是一款业界领先的三维树木植被建模软件&#xff0c;特别适用于游戏开发和影视制作。 一、基础操作 旋转&#xff1a;鼠标左键 平移&#xff1a;鼠标中键 缩放&#xff1a;鼠标中键滚动 Trunks树干节点 Branches树枝 Cap给树干封口 Frond创建大树叶 Decorations…

Golang的发展历程

Golang的发展历程可以分为以下几个阶段&#xff1a; 设计阶段&#xff1a;2007年&#xff0c;Google开始研究开发一种新的编程语言&#xff0c;主要出于对C和Java等编程语言的不足之处的反思。经过一年多的研究和讨论&#xff0c;Golang的设计方案得到确定&#xff0c;主要包括…

攻防世界 robots

开启场景 根据提示访问/robots.txt&#xff0c;发现了 f1ag_1s_h3re.php 拼接访问 /f1ag_1s_h3re.php 发现了 flag cyberpeace{d8b7025ed93ed79d44f64e94f2527a17}

智慧低空物流路径优化系统的Python实现:强化学习与遗传算法的深度结合

近年来&#xff0c;低空物流在城市配送中的重要性日益凸显。无人机技术的发展让“最后一公里”的问题找到了新的解决思路。然而&#xff0c;要让无人机真正成为物流的可靠工具&#xff0c;路径规划依然是一个极具挑战的难题——复杂的禁飞区、动态的天气变化、实时的配送需求波…

ESXi 软件架构 简介

这里写目录标题 ESXi 软件架构图VMkernel文件系统内存文件系统VMFS 分布式文件系统VMkernel 文件系统特点 用户和组标准虚拟交换机Standard Port Group NetworkVmkernel Network User WorldSwap 文件DCUIUser World 上的其他进程 ESXi 管理模型ESXi 管理模型设计的核心思想Syst…

最新的强大的文生视频模型Pyramid Flow 论文阅读及复现

《PYRAMIDAL FLOW MATCHING FOR EFFICIENT VIDEO GENERATIVE MODELING》 论文地址&#xff1a;2410.05954https://arxiv.org/pdf/2410.05954 项目地址&#xff1a; jy0205/Pyramid-Flow&#xff1a; 用于高效视频生成建模的金字塔流匹配代码https://github.com/jy0205/Pyram…

ESP32_H2(IDF)学习系列-ADC模数转换(连续转换)

一、简介&#xff08;节选手册&#xff09; 资料参考https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32h2/api-reference/peripherals/adc_calibration.html 1 概述 ESP32-H2 搭载了以下模拟外设&#xff1a; • 一个 12 位逐次逼近型模拟数字转换器 (SAR ADC)&…

“信任构建”:网上购物商城的用户评价与信誉系统

2 相关技术 2.1 SSM框架介绍 本课题程序开发使用到的框架技术&#xff0c;英文名称缩写是SSM&#xff0c;在JavaWeb开发中使用的流行框架有SSH、SSM、SpringMVC等&#xff0c;作为一个课题程序采用SSH框架也可以&#xff0c;SSM框架也可以&#xff0c;SpringMVC也可以。SSH框架…