ORM
- FastAPI 可与任何数据库和任何样式的库配合使用并和数据库通信
- object-relational mapping 对象关系映射
- ORM 具有在代码和数据库表(关系)中的对象之间进行转换(映射)的工具
- 使用 ORM,通常会创建一个表示 SQL 数据表的类,该类的每个属性都表示一个列,具有名称和类型
小栗子
- Pet 类可以表示 SQL 表 pets
- 并且 Pet 类的每个实例对象代表数据库中的一行数据
- 例如,对象 orion_cat(Pet 的一个实例)可以具有属性 orion_cat.type,用于列类型,属性的值可以是:猫
项目架构
. └── sql_app ├── __init__.py ├── curd.py ├── database.py ├── main.py ├── models.py └── schemas.py
前提
需要先安装 sqlalchemy
pip install sqlalchemy
使用 sqlite
- 后面的栗子,暂时跟着官网,先使用 sqlite 数据库来演示
- 后面有时候再通过 Mysql 来写多一篇文章
database.py 代码
# 1、导入 sqlalchemy 部分的包 from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker # 2、声明 database url SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" # SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" # 3、创建 sqlalchemy 引擎 engine = create_engine( url=SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} ) # 4、创建一个 database 会话 session = sessionmaker(autocommit=False, autoflush=False, bind=engine) # 5、返回一个 ORM Model Base = declarative_base()
声明 database 连接 url
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
第一行是 slite 连接 url
其他数据库连接 url 的写法
# sqlite-pysqlite 库 sqlite+pysqlite:///file_path # mysql-mysqldb 库 mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> # mysql-pymysql 库 mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] # mysql-mysqlconnector 库 mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> # oracle-cx_Oracle 库 oracle+cx_oracle://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]] # postgresql-pypostgresql 库 postgresql+pypostgresql://user:password@host:port/dbname[?key=value&key=value...] # SQL Server-PyODBC 库 mssql+pyodbc://<username>:<password>@<dsnname>
创建一个数据库引擎
engine = create_engine(
url=SQLALCHEMY_DATABASE_URL,
connect_args={"check_same_thread": False}
)
- {"check_same_thread": False} 仅适用于 SQlite,其他数据库不需要用到
- 默认情况下,SQLite 将只允许一个线程与其通信,假设每个线程只处理一个独立的请求
- 这是为了防止被不同的事物(对于不同的请求)共享相同的连接
- 但是在 FastAPI 中,使用普通函数 (def) 可以针对同一请求与数据库的多个线程进行交互,因此需要让 SQLite 知道它应该允许使用多线程
- 需要确保每个请求在依赖项中都有自己的数据库连接会话,因此不需要设置为同一个线程
创建一个数据库会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
- SessionLocal 类的每个实例都是一个数据库会话
- 但 sessionmaker 本身还不是数据库会话
- 但是一旦创建了 SessionLocal 类的实例,这个实例就会成为实际的数据库会话
- 将其命名为 SessionLocal ,方便区分从 SQLAlchemy 导入的 Session
- 稍后将使用 Session(从 SQLAlchemy 导入的那个)
创建一个 ORM 模型基类
Base = declarative_base()
后面会通过继承这个 Base 类,来创建每个数据库 Model,也称为 ORM Model
models.py 代码
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String from sqlalchemy.orm import relationship from .database import Base class User(Base): # 1、表名 __tablename__ = "users" # 2、类属性,每一个都代表数据表中的一列 # Column 就是列的意思 # Integer、String、Boolean 就是数据表中,列的类型 id = Column(Integer, primary_key=True, index=True, autoincrement=True) email = Column(String, unique=True, index=True) hashed_password = Column(String) is_active = Column(Boolean, default=True) items = relationship("Item", back_populates="owner") class Item(Base): __tablename__ = "items" id = Column(Integer, primary_key=True, index=True, autoincrement=True) title = Column(String, index=True) description = Column(String, index=True) owner_id = Column(Integer, ForeignKey("users.id")) owner = relationship("User", back_populates="items")
注意:有 autoincrement 就不要用 default 了哈
Column
列,一个属性代表数据表中的一列
常用参数
参数 | 作用 |
primary_key | 如果设为 True ,这列就是表的主键 |
unique | 如果设为 True ,这列不允许出现重复的值 |
index | 如果设为 True ,为这列创建索引,提升查询效率 |
nullable |
|
default | 为这列定义默认值 |
autoincrement |
如果设为 True ,这列自增 |
String、Integer、Boolean
代表数据表中每一列的数据类型
schemas.py 代码
背景
为了避免混淆 SQLAlchemy 模型和 Pydantic 模型之间,将使用文件 models.py 编写 SQLAlchemy 模型和文件 schemas.py 编写 Pydantic 模型
实际代码
from typing import List, Optional from pydantic import BaseModel # Item 的基类,表示创建和查询 Item 时共有的属性 class ItemBase(BaseModel): title: str description: Optional[str] = None # 创建 Item 时的 Model class ItemCreate(ItemBase): pass # 查询 Item 时的 Model class Item(ItemBase): id: int owner_id: int # 向 Pydantic 提供配置 class Config: # orm_mode 会告诉 Pydantic 模型读取数据,即使它不是字典,而是 ORM 模型(或任何其他具有属性的任意对象) orm_mode = True class UserBase(BaseModel): email: str class UserCreate(UserBase): password: str class User(UserBase): id: int is_active: bool items: List[Item] = [] class Config: orm_mode = True