文章目录
基于sqlmodel实现表之间的一对多关系,并创建模型类。
一对多关系
python
"""
表关系: 一对多
heros_t: 英雄表
id name cate_id
cate_t: 类别表
id name
一个类别可以对应多个英雄;
一个英雄只能属于一个类别;
"""
from typing import Annotated, Optional
# from sqlalchemy.orm import Relationship
from sqlmodel import Field, SQLModel, create_engine, Session, select, Relationship
# 创建英雄表的模型类
class Hero(SQLModel, table=True):
# 指定映射后的表名
__tablename__ = "hero_t"
# id: Optional[int] = Field(default=None, primary_key=True) python3.9 类型注释
id: int | None = Field(default=None, primary_key=True) # python3.10+ 后支持的类型注释
name: str = Field(..., index=True, unique=True, nullable=False, description="英雄姓名")
# 所属类别 int or None
cate_id: Optional[int] = Field(default=None, foreign_key="cate_t.id") # 外键,引用cate_t的id字段
# 关系, 从模型类对象层面关联
cate: Optional["Cate"] = Relationship(back_populates="heros")
# back_populates表示 从另一方 反向查询英雄的属性
# 打印对象时的输出
def __repr_args__(self):
# 必须返回列表
return [
("name", self.name)
]
# 定义类别模型类
class Cate(SQLModel, table=True):
__tablename__ = "cate_t"
id: int | None = Field(default=None, primary_key=True) # python3.10+ 支持的类型注释
name: str = Field(..., unique=True, index=True, description="类别名称")
# 模型类层面的关系关联
heros: list["Hero"] = Relationship(back_populates="cate")
# 打印对象时的输出
def __repr_args__(self):
return [
("id", self.id),
("name", self.name)
]
# 连接数据库,并创建所有的表
sqlite_file_name = "one_to_many.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
# echo= True 输出sqlalchemy日志
engine = create_engine(sqlite_url, echo=False, connect_args={"check_same_thread": True})
# 创建会话对象
def get_session():
with Session(engine) as session:
yield session
def create_db_and_tables():
SQLModel.metadata.drop_all(engine) # 删除所有的表及数据
SQLModel.metadata.create_all(engine)
def main():
# 创建所有的表
create_db_and_tables()
try:
# 获取生成器
generator = get_session()
session = next(generator)
# 创建英雄
hero1 = Hero(name="寒冰射手") # id为None
hero2 = Hero(name="赏金猎人")
hero3 = Hero(name="烬")
# 创建类别
cate1 = Cate(name="射手")
# (关系) 关联
hero1.cate = cate1
hero2.cate = cate1
hero3.cate = cate1
# insert into tables
session.add(hero1) # 添加一个, 同时会把关联的cate对象插入cate_t表
session.add_all([hero2, hero3]) # 添加多个
# 提交数据
session.commit()
# 查看提交后的变量
print(hero1)
print(f"heros: {[hero1, hero2, hero3]}") # 数据库创建的id会更新到对象中
# 强制加载数据库中的数据,以更新对象数据
session.refresh(hero1) # 并发修改db后,需要更新
session.refresh(hero2)
session.refresh(hero3)
# 查询类别,并关联出该类别的所有的英雄
cate_ = session.get(Cate, 1) # 查询id=1的类别
print(f"cate: {cate_} - {cate_.heros}")
# 查询一个英雄
statement = select(Hero).where(Hero.name=="寒冰射手").offset(0).limit(1) # 查询并分页
# 执行声明语句
hero_ = session.exec(statement).first() # .one()/.first()/.all()
print("hero_:", hero_, hero_.cate, hero_.cate_id)
# 联合查询
statement2 = select(Hero, Cate).where(Hero.cate_id == Cate.id).offset(0).limit(10)
results = session.exec(statement2).all()
print("results:", results)
# 继续执行生成器里yield后的逻辑,抛出异常
next(generator)
except StopIteration:
print("执行结束...")
if __name__ == "__main__":
main()