FastAPI操作关系型数据库

FastAPI可以和任何数据库和任意样式的库配合使用,这里看一下使用SQLAlchemy的示例。下面的示例很容易的调整为PostgreSQLMySQLSQLiteOracle等。当前示例中我们使用SQLite

ORM对象关系映射

FastAPI可以与任何数据库在任何样式的库中一起与数据库进行通信。一种常见的模式就是“ORM”:对象关系映射。所谓对象关系映射就是,ORM具有在代码和数据库表(“关系型”)中的对象中间转换(“映射”)的工具。即使用ORM,在SQL数据库创建一个代表映射的类,该类的每个数据代表数据表中的一个列,具有名称和类型。约定俗成:类使用大写如Pet,代表SQL为表的pets,该累的每个实例对象标识数据库中的一行数据。比如一个对象orion_cat(Pet的一个实例)可以有一个属性orion_cat.type, 对标数据库中的type列。并且该属性的值可以是其它,例如"cat"

文件结构

假设有个fastapi_sqlalchemy的项目,其下有一个包为sql_app,结构如下所示:

fastapi_sqlalchemy
├── sql_app
│   ├── __init__.py
│   ├── crud.py
│   ├── database.py
│   ├── main.py
│   ├── models.py
│   ├── requirements.txt
│   └── schemas.py
└── sql_app.db

既然是sql_app包,肯定少不了__init__.py,这里的初始化函数为空。其中依赖requirement.txt中为

fastapi==0.111.0
pydantic==2.7.4
SQLAlchemy==2.0.30

📢📢:这里使用的SQLAlchemy 为2.0以上版本,如果使用v1,则有些语法不同。

创建SQLAlchemy部件

首先看一下sql_app/database.py

创建SQLAlchemy引擎和初始化Base类

# @File : database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

# 来允许SQLite这样
engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 用于建立数据库SQLAlchemy模型models
Base = declarative_base()


创建一个SQLAlchemy 引擎,其中connect_args={“check_same_thread”: False} 仅用于SQLite,其他数据库不需要 。

默认情况下,SQLite 只允许一个线程与其通信,假设有多个线程的话,也只将处理一个独立的请求。这是为了防止意外地为不同的事物(不同的请求)共享相同的连接。但是在 FastAPI 中,使用普通函数(def)时,多个线程可以为同一个请求与数据库交互,所以我们需要使用connect_args={“check_same_thread”: False}来让SQLite允许这样。

此外,我们将确保每个请求都在依赖项中获得自己的数据库连接会话,因此不需要该默认机制。

连接到一个SQLite数据库,该文件在当前目录下的sql_app.db中

创建SessionLocal的时候,每个 SessionLocal 的类的实例都是一个数据库会话,当然该类本身还不是数据库会话。但是一旦我们创建了一个SessionLocal类的实例,这个实例将是实际的数据库会话。我们将它命名为SessionLocal是为了将它与我们从 SQLAlchemy 导入的Session区别开来。稍后我们将使用Session(从 SQLAlchemy 导入的那个)。要创建SessionLocal类,请使用函数sessionmaker

Base = declarative_base(),在后面的会继承这个Base类,来创建每个数据库模型或者类(ORM模型)

创建数据库模型models

在文件sql_app/models.py中,使用上一步创建的Base类派生子类来创建SQLAlchemy模型。
📢📢:

  1. SQLAlchemy中的“模型”指的的是和数据库交互的类和实例
  2. Pydantic中的“模型”指的是不同的东西,即数据验证、转换以及文档类和实例
# @File : models.py


from sql_app.database import Base

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship


class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)  # primary_key 为True标识主键
    email = Column(String, unique=True, index=True)  # unique 如果为True表示这列不允许出现重复的值,index=True为这列创建索引,提升查询效率
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)  # default=True 为这列设置默认值

    items = relationship("Item", back_populates="owner")


class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("users.id"))

    owner = relationship("User", back_populates="items")

  1. 这个__tablename__属性是用来告诉 SQLAlchemy 要在数据库中为每个模型使用的数据库表的名称
  2. 使用Column来表示 SQLAlchemy 中的默认值。传递一个 SQLAlchemy “类型”,如Integer、String和Boolean,它定义了数据库中的类型,作为参数,以及其他限定。这些属性中的每一个都代表其相应数据库表中的一列
  3. 关系或者说内联外联的关系,使用relationship,当访问 user 中的属性items时,如 中my_user.items,它将有一个ItemSQLAlchemy 模型列表(来自items表),这些模型具有指向users表中此记录的外键。当您访问my_user.items时,SQLAlchemy 实际上会从items表中的获取一批记录并在此处填充进去。同样,当访问 Item中的属性owner时,它将包含表中的UserSQLAlchemy 模型users。使用owner_id属性/列及其外键来了解要从users表中获取哪条记录

创建Pydantic模型

接下来看看sql_app/shchemas.py
📢📢:为了避免 SQLAlchemy模型和 Pydantic模型之间的混淆,我们将有models.py(SQLAlchemy 模型的文件)和schemas.py( Pydantic 模型的文件)。这些 Pydantic 模型或多或少地定义了一个“schema”(一个有效的数据形状)。因此,这将帮助我们在使用两者时避免混淆。

创建初始化Pydantic模式/模型

创建一个ItemBaseUserBase Pydantic模型(或者我们说“schema”),他们拥有创建或读取数据时具有的共同属性。然后创建一个继承自他们的ItemCreateUserCreate,并添加创建时所需的其他数据(或属性)。因此在创建时也应当有一个password属性。但是为了安全起见,password不会出现在其他同类 Pydantic模型中,例如通过API读取一个用户数据时,它不应当包含在内。

# @File : schemas.py
from pydantic import BaseModel


class ItemBase(BaseModel):
    title: str
    description: str | None = None


class UserBase(BaseModel):
    email: str


class ItemCreate(ItemBase):
    pass


class Item(ItemBase):
    id: int
    owner_id: int

    class Config:
        # orm_mode = True
        from_attributes = True


class UserCreate(UserBase):
    password: str


class User(UserBase):
    id: int
    is_active: bool
    items: list[Item] = []

    class Config:
        # orm_mode = True
        # 解决pydantic/_internal/_config.py:334: UserWarning: Valid config keys have changed in V2:
        # * 'orm_mode' has been renamed to 'from_attributes'
        from_attributes = True

先介绍下Config类的作用

    class Config:
        # orm_mode = True
        from_attributes = True

如果是Pydantic 是v1版本,则使用orm=True, 本机环境安装的是v2+版本。这个类是一个Pydantic配置项。orm_mode或者from_attributes 会告诉Pydantic模型读取数据,而不仅仅是字典,而是一个ORM模型(或者说一个具有属性的对象)。这样,而不是仅仅试图从dict上 id 中获取值,如下所示:

id = data["id"]

它还会尝试从属性中获取它,如:

id = data.id

有了这个,Pydantic模型与 ORM 兼容,您只需在路径操作response_model的参数中声明它即可。将能够返回一个数据库模型,它将从中读取数据

📢📢:SQLAlchemy 风格和 Pydantic 风格¶
请注意,SQLAlchemy模型使用 =来定义属性,并将类型作为参数传递给Column,例如:

name = Column(String)

虽然 Pydantic模型使用: 声明类型,但新的类型注释语法/类型提示是:

name: str

请牢记这一点,这样您在使用:还是=时就不会感到困惑

CRUD工具

现在让我们看看文件sql_app/crud.py。在这个文件中,我们将编写可重用的函数用来与数据库中的数据进行交互。

CRUD分别为:增加(Create)、查询(Read)、更改(Update)、删除(Delete),即增删改查。

…虽然在这个例子中我们只是新增和查询。

读取数据¶
sqlalchemy.orm中导入Session,这将允许您声明db参数的类型,并在您的函数中进行更好的类型检查和完成。导入之前的models(SQLAlchemy 模型)和schemas(Pydantic模型/模式)。

创建一些工具函数来完成:

  • 通过 ID 和电子邮件查询单个用户。
  • 查询多个用户。
  • 查询多个项目
# @File : crud.py
from sqlalchemy.orm import Session

from sql_app import models, schemas


def get_user(db: Session, user_id: int):
    return db.query(models.User).filter(models.User.id == user_id).first()


def get_user_by_email(db: Session, email: str):
    return db.query(models.User).filter(models.User.email == email).first()


def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.User).offset(skip).limit(limit).all()


def create_user(db: Session, user: schemas.UserCreate):
    fake_hashed_password = user.password + "notreallyhashed"
    db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user


def get_items(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.Item).offset(skip).limit(limit).all()


def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
    db_item = models.Item(**item.dict(), owner_id=user_id)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item

📢📢:Tip

这里不是将每个关键字参数传递给Item并从Pydantic模型中读取每个参数,而是先生成一个字典,其中包含Pydantic模型的数据:

item.dict()

然后我们将dict的键值对 作为关键字参数传递给 SQLAlchemy Item:

Item(**item.dict())

然后我们传递 Pydantic模型未提供的额外关键字参数owner_id:

Item(**item.dict(), owner_id=user_id)

因此创建数据的步骤即为:
现在创建工具函数来创建数据。

  1. 使用您的数据创建一个 SQLAlchemy 模型实例。
  2. 使用add来将该实例对象添加到数据库会话。
  3. 使用commit来将更改提交到数据库(以便保存它们)。
  4. 使用refresh来刷新您的实例对象(以便它包含来自数据库的任何新数据,例如生成的 ID)

FastAPI应用程序

现在在sql_app/main.py文件中 让我们集成和使用我们之前创建的所有其他部分。

from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from sql_app import schemas, crud, models
from sql_app.database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()


# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)


@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users


@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user


@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
    user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
    return crud.create_user_item(db=db, item=item, user_id=user_id)


@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    items = crud.get_items(db, skip=skip, limit=limit)
    return items

现在使用我们在sql_app/database.py文件中创建的SessionLocal来创建依赖项。我们需要每个请求有一个独立的数据库会话/连接(SessionLocal),在整个请求中使用相同的会话,然后在请求完成后关闭它。然后将为下一个请求创建一个新会话。为此,我们将创建一个包含yield的依赖项,正如前面关于Dependencies with yield的部分中所解释的那样。我们的依赖项将创建一个新的 SQLAlchemy SessionLocal,它将在单个请求中使用,然后在请求完成后关闭它。

📢📢:参数db实际上是 type SessionLocal,但是这个类(用 创建sessionmaker())是 SQLAlchemy 的“代理” Session,所以,编辑器并不真正知道提供了哪些方法。

但是通过将类型声明为Session,编辑器现在可以知道可用的方法(.add()、.query()、.commit()等)并且可以提供更好的支持(比如完成)。类型声明不影响实际对象。

运行

# 执行命令
% uvicorn sql_app.main:app --reload

在这里插入图片描述
打开浏览器:http://127.0.0.1:8000/docs
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vulnhub-DC-8

靶机IP:192.168.20.143 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 信息收集 用nmap和wappalyzer收集下信息 发现是Drupal 7网站 dirsearch扫下目录 ┌──(root㉿kali)-[/home/kali/Desktop] └─# dirsearch -u http://192.168.20…

【Spring EL<二>✈️✈️ 】SL 表达式结合 AOP 注解实现鉴权

目录 🍻前言 🍸一、鉴权(Authorization) 🍺二、功能实现 2.1 环境准备 2.2 代码实现 2.3 测试接口 🍹三、测试功能 3.1 传递 admin 请求 ​ 3.2 传递普通 user 请求 🍻四、章末 &a…

eFuse电子保险丝,需要了解的技术干货来啦

热保险丝作为一种基本的电路保护器件,已经成功使用了150多年。热保险丝有效可靠、易用,具有各种不同的数值和版本,能够满足不同的设计目标。然而,对于寻求以极快的速度切断电流的设计人员来说,热保险丝不可避免的缺点就…

t265 jetpack 6 px4 ros2

Ubuntu22.04 realsenseSDK2和ROS2Wrapper安装方法,包含T265版本踩坑问题_ros2 realsense-CSDN博客 210 git clone https://github.com/IntelRealSense/librealsense.git 212 git branch 215 git tag 218 git checkout v2.51.1 219 git branch 265 git clone https://…

法考报名照片审核不通过?原因看过来

法考报名照片审核不通过?原因看过来👀

API工具--Apifox和Postman对比(区别)

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

Python 俄罗斯方块小游戏【含Python源码 MX_007期】

系统简介: 俄罗斯方块是一款经典的俄罗斯益智游戏,由苏联工程师阿列克谢帕基特诺夫(Alexey Pajitnov)于1984年创建。在游戏中,玩家需要操纵不同形状的方块,以水平移动和旋转的方式,使它们在屏幕…

Python学习从0开始——Kaggle时间序列002

Python学习从0开始——Kaggle时间序列002 一、作为特征的时间序列1.串行依赖周期 2.滞后序列和滞后图滞后图选择滞后 3.示例 二、混合模型1.介绍2.组件和残差3.残差混合预测4.设计混合模型5.使用 三、使用机器学习进行预测1.定义预测任务2.为预测准备数据3.多步骤预测策略3.1 M…

Pytorch安装

一、window 下载CUDA Driver nvidia-driver(NVIDIA驱动程序)是操作系统与NVIDIA GPU硬件之间的软件接口。它负责管理GPU硬件的操作、资源分配、性能优化等任务。CUDA依赖于NVIDIA的驱动程序,因为它需要与GPU硬件进行通信以执行并行计算任务。在使用CUDA进行开发时,确保安装了…

Qt多线程之moveToThread()函数

文章目录 一、moveToThread()执行后,当前代码线程没有改变。二、对象执行moveToThread()后,哪些成员加入了子线程1、创建对象时不指定父对象2、对属性对象使用moveToThread加入子线程作用域3、将属性对象的创建放到子线程中执行 三、C内存模型 在使用“继…

基于改进下垂控制的微电网控制研究(matlab)

主要内容 该模型为simulink仿真模型,主要实现的内容如下: 模型比较全面,包括蓄电池、超级电容和光伏发电模块,同时包括阻性负载和冲击负载,能够较好的了解这些模块对于母线电压特性的研究。采用改进的下垂控制策略…

嘉之音真丝彩绘吸音画,把记忆与向往刻进艺术里

那一瞬间定格在记忆中 那一刹那感动到骨髓里 曾经 现在 将来 每一幅画,都是一个故事的开始,一段记忆的延续,它们承载着过去,映照着现在,启迪着未来。在艺术的世界里,每个人都能找到属于自己的那一幅画…

STM32F103ZET6_HAL_CAN

1定义时钟 2定义按键 按键上拉电阻 3开启串口 4打开CAN(具体什么意思上一篇讲了) 5生成代码 /* USER CODE BEGIN Header */ /********************************************************************************* file : main.c* brief …

HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)

系列文章目录 HarmonyOS Next 系列之省市区弹窗选择器实现(一) HarmonyOS Next 系列之验证码输入组件实现(二) HarmonyOS Next 系列之底部标签栏TabBar实现(三) HarmonyOS Next 系列之HTTP请求封装和Token…

2024年6月14日 十二生肖 今日运势

小运播报:2024年6月14日,星期五,农历五月初九 (甲辰年庚午月己酉日),法定工作日。今天世界献血日,捐献新鲜血液,挽救更多生命,每位献血者都是英雄! 红榜生肖…

云渲染动画:C4D如何正确渲染导出动画?

​C4D是一款功能强大的3D建模、动画和渲染软件,在制作动画时,正确的渲染和导出流程至关重要,以确保动画质量和流畅性。 帧率概念 动画就是一幅幅图片连贯起来,30帧/秒,就是一秒出现30张图片一般国外都是30&#xff0c…

打破数据分析壁垒:SPSS复习必备(一)

一、数据录入与数据获取 1.变量的测量尺度 (1)定类尺度 顾名思义,是对事物的类别或属性的一种测度,按照事物的某种属性对其进行分类或分组。 该类变量只能计算频数和频率,用表示 (2)定序尺…

table组件,前端如何使用table组件,打印数组数据,后端传输的数据应该如何打印

一、如何使用table,将数组数据打印出来 后端传来的数据,很大概率是一个List数组,我们必须用一个table组件,来打印这些数据。 table标签的介绍 在HTML中,table是常用组件之一,主要用来打印数组信息。 它的…

yolo-inference多后端+多任务+多算法+多精度模型 框架开发记录(python版)

先贴出github地址,欢迎大家批评指正:https://github.com/taifyang/yolo-inference 不知不觉LZ已经快工作两年了,由于之前的工作内容主要和模型部署相关,想着利用闲暇时间写一些推理方面的经验总结,于是有了这个工程。其…

【Pytorch】一文向您详细介绍 torch.nn.DataParallel() 的作用和用法

【Pytorch】一文向您详细介绍 torch.nn.DataParallel() 的作用和用法 下滑查看解决方法 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介:985高…