FastAPI:(11)SQL数据库

FastAPI:(11)SQL数据库

由于CSDN无法展示「渐构」的「#d,#e,#t,#c,#v,#a」标签,推荐访问我个人网站进行阅读:Hkini
「渐构展示」如下:在这里插入图片描述
在这里插入图片描述

#c 概述 文章内容概括

FastAPI数据库
SQLModel
数据库操作
数据模型
表模型
响应模型
会话管理
增删改查

1.安装SQLModel

#d SQLModel

SQLModel 是一个用于在 Python 中声明数据库模型的库,它结合了 SQLAlchemy 的 ORM 能力与 Pydantic 的数据验证特性。它旨在提供一种简单、统一的方式来定义数据库表结构,同时用于数据序列化、验证和交互,主要用于与 FastAPI 等现代 Web 框架集成。
安装SQLModel通过pip install sqlmodel

重要特征:

  • 双重用途(ORM + 数据验证):同时兼容 SQLAlchemy 的 ORM 和 Pydantic 的数据校验与序列化。
  • 基于类型注解:使用 Python 的类型提示定义字段和数据结构,更直观地说明数据模型。
  • 自动生成表结构:可通过模型类定义自动生成数据库表。
  • 对异步和同步均友好:兼容异步和同步数据库操作,适应不同项目架构。
  • 简化模型继承与组合:可继承基础类构建只读模型、创建数据模型、更新模型等多种用途。

#e 电子病历模型(正例) SQLModel

例子描述
医院系统中的电子病历模型 MedicalRecord,可表示为数据库表,也可用于接收医生上传记录或给前端返回。字段如 patient_id: int, diagnosis: str, record_time: datetime,模型用于数据库操作和前后端数据传输,完全符合 SQLModel 的理念。

特征对比

  • 双重用途:模型同时作为数据库表结构与 API 的请求/响应模型。
  • 基于类型注解:所有字段通过类型注解标明类型。
  • 表结构生成:可通过该模型自动创建数据表。
  • 异步友好:可用于 FastAPI 异步接口处理。
from sqlmodel import Field, SQLModel, create_engine, Session
from fastapi import FastAPI
from typing import Optional
from datetime import datetimeclass MedicalRecord(SQLModel, table=True):id: Optional[int] = Field(default=None, primary_key=True)patient_id: intdiagnosis: strrecord_time: datetimesqlite_url = "sqlite:///./test.db"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)app = FastAPI()@app.post("/records/")
def create_record(record: MedicalRecord):with Session(engine) as session:session.add(record)session.commit()session.refresh(record)return record

#e 商品库存模型(正例) SQLModel

例子描述
电商平台中的 InventoryItem 模型记录商品 ID、库存数量、价格等信息。该模型用于库存管理模块的数据表定义,且用于与 API 交互中的库存变更和查询,统一使用。

特征对比

  • 双重用途:模型用于数据库记录和接口的数据通信。
  • 基于类型注解:如 quantity: int, price: float
  • 自动生成表结构:可以直接映射为 inventory_items 表。
  • 简化模型继承:可以轻松派生创建更新库存的模型。
from sqlmodel import SQLModel, Field, create_engine, Session
from fastapi import FastAPI
from typing import Optionalclass InventoryItem(SQLModel, table=True):id: Optional[int] = Field(default=None, primary_key=True)name: strquantity: intprice: floatengine = create_engine("sqlite:///./inventory.db", echo=True)
SQLModel.metadata.create_all(engine)app = FastAPI()@app.post("/items/")
def add_item(item: InventoryItem):with Session(engine) as session:session.add(item)session.commit()session.refresh(item)return item

#e 外部API响应模型(反例) SQLModel

例子描述
一个天气服务中,用于解析外部 API 返回数据的 WeatherResponse 模型,字段如 temperature: float, humidity: float, location: str。该模型只用于临时解析 JSON 响应数据,不涉及数据库存储,也不参与任何数据写入操作。

特征对比

  • 双重用途缺失:仅用于解析数据,不能表示数据库表。
  • 缺乏 ORM 功能:字段虽有类型注解,但无数据库元数据。
  • 无表结构生成意义:该数据模型不参与数据库操作。
  • Pydantic 使用合理:仅作为数据验证使用,适合用 Pydantic 而非 SQLModel。
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optionalapp = FastAPI()# 外部天气服务返回的数据格式
class WeatherResponse(BaseModel):location: strtemperature: floathumidity: floatdescription: Optional[str]# 模拟调用外部 API,返回数据结构
@app.get("/weather/", response_model=WeatherResponse)
def get_weather():# 模拟外部 API 响应fake_api_data = {"location": "Chengdu","temperature": 28.5,"humidity": 70.2,"description": "Cloudy"}return fake_api_data

2.单一模型

#e 官网例子(正例) SQLModel

具体步骤:

  1. 创建模型
  2. 创建引擎
  3. 创建表
  4. 创建会话(Session)依赖项:Session 会存储内存中的对象并跟踪数据中所需更改的内容,然后它使用 engine 与数据库进行通信。使用 yield 创建一个 FastAPI 依赖项,为每个请求提供一个新的 Session 。这确保每个请求使用一个单独的会话。
  5. 启动创建表:对于生产环境,可能会用一个能够在启动应用程序之前运行的迁移脚本如Alembic
  6. 创建Hero类:因为每个 SQLModel 模型同时也是一个 Pydantic 模型,所以可以在与 Pydantic 模型相同的类型注释中使用它。
  7. 读取Hero类
  8. 读取单个Hero
  9. 删除单个Hero
from typing import Annotatedfrom fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, selectclass Hero(SQLModel, table=True): # 利用SQLModel创建数据库模型,table=True,告诉SQLModel这是一个表模型id: int | None = Field(default=None, primary_key=True) # primary_key表示是主键;int | None 数据在SQL应该的是INTEGER并且NULLABLE name: str = Field(index=True) # index=True 创建SQL索引,str在数据库中将会是TEXT或者VARCHARTage: int | None = Field(default=None, index=True)secret_name: strsqlite_file_name = "database.db" # 创建数据库引擎,用来与数据库保持连接
sqlite_url = f"sqlite:///{sqlite_file_name}"connect_args = {"check_same_thread": False} # 不同线程中使用同一个SQLite数据库,将会按照代码结构确保「每个请求使用一个单独的SQLModel会话」
engine = create_engine(sqlite_url, connect_args=connect_args)def create_db_and_tables(): # 创建表SQLModel.metadata.create_all(engine)def get_session(): # 创建Session会话依赖项with Session(engine) as session:yield session # 为每个请求提供一个新的SessionSessionDep = Annotated[Session, Depends(get_session)]app = FastAPI()@app.on_event("startup") # 启动时床啊金数据库表
def on_startup():create_db_and_tables()@app.post("/heroes/") # 创建Hero类,声明一个Hero参数,将从json主体中读取数据,同样可以声明为「返回类型」,自动生成API文档界面
def create_hero(hero: Hero, session: SessionDep) -> Hero:session.add(hero)session.commit()session.refresh(hero)return hero@app.get("/heroes/") # 读取「Hero」类,并利用limit和offset对结果进行分页
def read_heroes(session: SessionDep,offset: int = 0,limit: Annotated[int, Query(le=100)] = 100,
) -> list[Hero]:heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()return heroes@app.get("/heroes/{hero_id}") # 读取单个「Hero」
def read_hero(hero_id: int, session: SessionDep) -> Hero:hero = session.get(Hero, hero_id)if not hero:raise HTTPException(status_code=404, detail="Hero not found")return hero@app.delete("/heroes/{hero_id}") # 删除单个「Hero」
def delete_hero(hero_id: int, session: SessionDep):hero = session.get(Hero, hero_id)if not hero:raise HTTPException(status_code=404, detail="Hero not found")session.delete(hero)session.commit()return {"ok": True}

3.多个模型

#c 说明 多个模型

现在稍微重构一下这个应用,以提高安全性多功能性

如果查看之前的应用程序,可以在 UI 界面中看到,到目前为止,由客户端决定要创建的 Heroid 值。

不应该允许这样做,因为可能会覆盖在数据库中已经分配的 id 。决定 id 的行为应该由后端数据库来完成,而非客户端

此外,为 hero 创建了一个 secret_name ,但到目前为止,在各处都返回了它,这就不太秘密了……😅

通过添加一些额外的模型来解决这些问题,而 SQLModel 将在这里大放异彩。

#e 官网多模型例子(正例) SQLModel

操作步骤:

  1. 创建HeroBase基类
  2. 创建Hero表模型
  3. 创建HeroPublic公共数据模型
  4. 创建Hero的数据创建模型HeroCreate
  5. 创建Hero的更新模型HeroUpdateHeroUpdate 数据模型有些特殊,它包含创建新 hero 所需的所有相同字段,但所有字段都是可选的(都有默认值)。这样,当更新一个 hero 时,可以只发送您想要更新的字段。因为所有字段实际上都发生了变化(类型现在包括 None ,并且现在有一个默认值 None ),需要重新声明它们,重新声明所有字段,因此并不是真的需要从 HeroBase 继承。让它继承只是为了保持一致,但这并不必要。这更多是个人喜好的问题。
  6. 使用HeroCreate创建并返回HeroPublic:在请求中接收到一个 HeroCreate 数据模型,然后从中创建一个 Hero 表模型。这个新的表模型 Hero 会包含客户端发送的字段,以及一个由数据库生成的 id 。然后将与函数中相同的表模型 Hero 原样返回。但是由于使用 HeroPublic 数据模型声明了 response_modelFastAPI 会使用 HeroPublic 来验证和序列化数据。
  7. HeroPublic读取Hero
  8. HeroPublic读取单个Hero
  9. HeroPublic更新单个Hero
from typing import Annotatedfrom fastapi import Depends, FastAPI, HTTPException, Query
from sqlmodel import Field, Session, SQLModel, create_engine, selectclass HeroBase(SQLModel): # 创建HeroBase基类,「共享字段」name,agename: str = Field(index=True)age: int | None = Field(default=None, index=True)class Hero(HeroBase, table=True): # 创建Hero表模型,继承HeroBase的共享字段id: int | None = Field(default=None, primary_key=True)secret_name: strclass HeroPublic(HeroBase): # 创建返回给API客户端的HeroPublic模型,不包括secret_name,保护Heroid: int # 重新声明 `id: int` 。这样便与 API 客户端建立了一种约定,始终可以期待 `id` 存在并且是一个整数 `int`(永远不会是 `None` )class HeroCreate(HeroBase): # 创建用于创建hero的数据模型secret_name: str #不仅拥有与 `HeroBase` 相同的字段,还有 `secret_name` 。当客户端创建一个新的hero时,会送 `secret_name` ,被存储到数据库中,但这些 `secret_name` 不会通过 API 返回给客户端class HeroUpdate(HeroBase): # 创建用于更新Hero的数据模型name: str | None = Noneage: int | None = Nonesecret_name: str | None = Nonesqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, connect_args=connect_args)def create_db_and_tables():SQLModel.metadata.create_all(engine)def get_session():with Session(engine) as session:yield sessionSessionDep = Annotated[Session, Depends(get_session)]
app = FastAPI()@app.on_event("startup")
def on_startup():create_db_and_tables()@app.post("/heroes/", response_model=HeroPublic) # 使用HeroCreate创建,并返回HeroPublic
def create_hero(hero: HeroCreate, session: SessionDep):db_hero = Hero.model_validate(hero)session.add(db_hero)session.commit()session.refresh(db_hero)return db_hero # 将与函数中相同的表模型 `Hero` 原样返回。但是由于使用 `HeroPublic` 数据模型声明了 `response_model` ,**FastAPI** 会使用 `HeroPublic` 来验证和序列化数据。@app.get("/heroes/", response_model=list[HeroPublic]) # 用HeroPublic读取Hero表,使用 `response_model=list[HeroPublic]` 确保正确地验证和序列化数据。
def read_heroes(session: SessionDep,offset: int = 0,limit: Annotated[int, Query(le=100)] = 100,
):heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()return heroes@app.get("/heroes/{hero_id}", response_model=HeroPublic) #用HeroPublic读取单个Hero
def read_hero(hero_id: int, session: SessionDep):hero = session.get(Hero, hero_id)if not hero:raise HTTPException(status_code=404, detail="Hero not found")return hero@app.patch("/heroes/{hero_id}", response_model=HeroPublic) #用`HeroUpdate` 更新单个 Hero
def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep):hero_db = session.get(Hero, hero_id)if not hero_db:raise HTTPException(status_code=404, detail="Hero not found")hero_data = hero.model_dump(exclude_unset=True) #在代码中,会得到一个 `dict` ,其中包含客户端发送的所有数据,只有客户端发送的数据,并排除了任何一个仅仅作为默认值存在的值。为此,用 `exclude_unset=True` 。这是最主要的技巧。hero_db.sqlmodel_update(hero_data) # 利用 `hero_data` 的数据更新 `hero_db`session.add(hero_db)session.commit()session.refresh(hero_db)return hero_db@app.delete("/heroes/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):hero = session.get(Hero, hero_id)if not hero:raise HTTPException(status_code=404, detail="Hero not found")session.delete(hero)session.commit()return {"ok": True}

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

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

相关文章

“智眸·家联“项目开发(一)

嵌入式开发调试知识点总结(含操作流程) 我们今天解决问题的过程,就像是侦探破案,从最表面的线索(网络不通)开始,一步步深入,最终找到了案件的核心(硬件不匹配&#xff0…

展开说说Android之Retrofit详解_使用篇

Retrofit是由Square公司开发的类型安全HTTP客户端框架,借助动态代理在运行时生成接口实现类,将注解转化为OkHttp请求配置;节省成本通过转换器(Gson/Moshi)自动序列化JSON/XML,内部处理网络请求在主线程返回报文。Retrofit 直译是封…

复古美学浅绿色文艺风格Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色介绍 复古美学浅绿色文艺风格 Lr 调色,是基于 Adobe Lightroom(Lr)软件,为摄影作品赋予特定艺术氛围的调色方式。通过合理设置软件中的各项参数与工具,把照片调整为以浅绿色为主调,融合复古元素与文艺气…

力扣网C语言编程题:缺失的第一个正数第三种解题方法

一. 简介 前面文章学习了对该题目的两种解题思路,文章如下: 力扣网C语言编程题:缺失的第一个正数-CSDN博客 但是前面的实现上在空间复杂度上没有满足要求。本文学习一种在空间复杂度上为 O(1)的思路。 二. 力扣网C语言编程题:缺…

PyTorch 实现 MNIST 手写数字识别

PyTorch 实现 MNIST 手写数字识别 MNIST 是一个经典的手写数字数据集,包含 60000 张训练图像和 10000 张测试图像。使用 PyTorch 实现 MNIST 分类通常包括数据加载、模型构建、训练和评估几个部分。 数据加载与预处理 使用 torchvision 加载 MNIST 数据集&#x…

Python内存互斥与共享深度探索:从GIL到分布式内存的实战之旅

引言:并发编程的内存困局 在开发高性能Python应用时,我遭遇了这样的困境:多进程间需要共享百万级数据,而多线程间又需保证数据一致性。传统解决方案要么性能低下,要么引发竞态条件。本文将深入探讨Python内存互斥与共…

【Unity】使用 C# SerialPort 进行串口通信

索引 一、SerialPort串口通信二、使用SerialPort1.创建SerialPort对象,进行基本配置2.写入串口数据①.写入串口数据的方法②.封装数据 3.读取串口数据①.读取串口数据的方法②.解析数据 4.读取串口数据的时机①.DataReceived事件②.多线程接收数据 5.粘包问题处理 一…

如何写好单元测试:Mock 脱离数据库,告别 @SpringBootTest 的重型启动

如何写好单元测试:Mock 脱离数据库,告别 SpringBootTest 的重型启动 作者:Killian(重庆) — 欢迎各位架构猎头、技术布道者联系我,项目实战丰富,代码稳健,Mock测试爱好者。 技术栈&a…

【DNS】在 Windows 下修改 `hosts` 文件

在 Windows 下修改 hosts 文件,一般用于本地 DNS 覆盖。操作步骤如下(以 Windows 10/11 为例): 1. 以管理员权限打开记事本 点击 开始 → 输入 “记事本”在“记事本”图标上右键 → 选择 以管理员身份运行 如果提示“是否允许此…

共享内存实现进程通信

目录 system V共享内存 共享内存示意图 共享内存函数 shmget函数 shmat函数 shmdt函数 shmctl函数 代码示例 shm头文件 构造函数 获取key值 创建者的构造方式 GetShmHelper 函数 GetShmUseCreate 函数 使用者的构造方式 GetShmForUse 函数 分离附加操作 DetachShm 函数 AttachS…

6月15日星期日早报简报微语报早读

6月15日星期日,农历五月二十,早报#微语早读。 1、证监会拟修订期货公司分类评价:明确扣分标准,优化加分标准; 2、国家考古遗址公园再添10家,全国已评定65家; 3、北京多所高校禁用罗马仕充电宝…

破解关键领域软件测试“三重难题”:安全、复杂性、保密性

在国家关键领域,软件系统正成为核心战斗力的一部分。相比通用软件,关键领域软件在 安全性、复杂性、实时性、保密性 等方面要求极高。如何保障安全合规前提下提升测试效率,确保系统稳定,已成为软件质量保障的核心挑战。 关键领域…

记录一次 Oracle DG 异常停库问题解决过程

记录一次 Oracle DG 异常停库问题解决过程 某医院有以下架构的双节点 Oracle 集群: 节点1:172.16.20.2 节点2:172.16.20.3 SCAN IP:172.16.20.1 DG:172.16.20.1206月12日,医院信息科用户反映无法连接 DG 服务器。 登录 DG 服务…

MySQL使用EXPLAIN命令查看SQL的执行计划

1‌、EXPLAIN 的语法 MySQL 中的 EXPLAIN 命令是用于分析 SQL 查询执行计划的关键工具,它能帮助开发者理解查询的执行方式并找出性能瓶颈‌‌。 语法格式: EXPLAIN <sql语句> 【示例】查询学生表关联班级表的执行计划。 (1)创建班级信息表和学生信息表,并创建索…

Go语言2个协程交替打印

WaitGroup 无缓冲channel waitgroup 用来控制2个协程 Add() 、Done()、Wait() channel用来实现信号的传递和信号的打印 ch1: 用来记录打印的信号 ch2:用来实现信号的传递&#xff0c;实现2个协程的顺序打印 package mainimport ("fmt""sync" )func ma…

微信小程序 路由跳转

路由方式 官方参考文档 wx.switchTab 实现底部导航栏 1.配置信息 app.json"tabBar": {"custom": true,"list": [{"pagePath": "pages/home/index","text": "首页"},{"pagePath": "p…

[Java 基础]正则表达式

正则表达式是一种强大的文本模式匹配工具&#xff0c;它使用一种特殊的语法来描述要搜索或操作的字符串模式。在 Java 中&#xff0c;我们可以使用 java.util.regex包提供的类来处理正则表达式。 :::color3 正则表达式不止 Java 语言提供了相应的功能&#xff0c;很多其他语言…

ArcGIS安装出现1606错误解决办法

问题背景&#xff1a; 由于最近Arcgis10.2打是有些功能不正常退出&#xff0c;比如arctoolbox中的&#xff0c;table to excel 功能&#xff0c;只要一点击&#xff0c;arcgis就报错退出&#xff0c;平常在使用过程中&#xff0c;也经常出现一些莫名其妙的崩溃现象&#xff0c…

wpf 解决DataGridTemplateColumn中width绑定失效问题

感谢酪酪烤奶 提供的Solution 文章目录 感谢酪酪烤奶 提供的Solution使用示例示例代码分析各类交互流程 WPF DataGrid 列宽绑定机制分析整体架构数据流分析1. ViewModel到Slider的绑定2. ViewModel到DataGrid列的绑定a. 绑定代理(BindingProxy)b. 列宽绑定c. 数据流 关键机制详…

语音转文本ASR、文本转语音TTS

ASR Automatic Speech Recognition&#xff0c;语音转文本。 技术难点&#xff1a; 声学多样性 口音、方言、语速、背景噪声会影响识别准确性&#xff1b;多人对话场景&#xff08;如会议录音&#xff09;需要区分说话人并分离语音。 语言模型适配 专业术语或网络新词需要动…