SQLModel 多对多关系实现详解
多对多关系简介
在关系型数据库中,多对多关系是一种常见的数据关联方式。它表示一个实体可以与多个其他实体相关联,反之亦然。在SQLModel中,我们可以通过中间表(关联表)来实现这种关系。
核心模型定义
中间表模型
class HeroTeamLink(SQLModel, table=True):
team_id: Optional[int] = Field(
default=None, foreign_key="team.id", primary_key=True
)
hero_id: Optional[int] = Field(
default=None, foreign_key="hero.id", primary_key=True
)
这个中间表模型HeroTeamLink
定义了英雄和团队之间的关联关系。它包含两个字段:
team_id
: 外键关联到Team表的id,同时也是主键的一部分hero_id
: 外键关联到Hero表的id,同时也是主键的一部分
这种复合主键的设计确保了每个英雄-团队组合的唯一性。
团队模型
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
heroes: List["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink)
团队模型定义了:
- 基本字段:id、name(有索引)、headquarters
- 关系字段:heroes,通过Relationship指定与Hero模型的多对多关系
back_populates="teams"
表示双向关系link_model=HeroTeamLink
指定了中间表模型
英雄模型
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
teams: List[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink)
英雄模型定义了:
- 基本字段:id、name(有索引)、secret_name、age(可选且有索引)
- 关系字段:teams,同样通过Relationship建立与Team模型的多对多关系
数据库初始化
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
这部分代码完成了:
- 指定SQLite数据库文件位置
- 创建数据库引擎,设置echo=True以便查看SQL语句
- 定义函数创建所有表结构
数据操作示例
def create_heroes():
with Session(engine) as session:
# 创建团队
team_preventers = Team(name="Preventers", headquarters="Sharp Tower")
team_z_force = Team(name="Z-Force", headquarters="Sister Margaret's Bar")
# 创建英雄并关联团队
hero_deadpond = Hero(
name="Deadpond",
secret_name="Dive Wilson",
teams=[team_z_force, team_preventers],
)
hero_rusty_man = Hero(
name="Rusty-Man",
secret_name="Tommy Sharp",
age=48,
teams=[team_preventers],
)
hero_spider_boy = Hero(
name="Spider-Boy", secret_name="Pedro Parqueador", teams=[team_preventers]
)
# 添加到会话并提交
session.add(hero_deadpond)
session.add(hero_rusty_man)
session.add(hero_spider_boy)
session.commit()
# 刷新对象以获取数据库生成的值
session.refresh(hero_deadpond)
session.refresh(hero_rusty_man)
session.refresh(hero_spider_boy)
# 打印结果
print("Deadpond:", hero_deadpond)
print("Deadpond teams:", hero_deadpond.teams)
print("Rusty-Man:", hero_rusty_man)
print("Rusty-Man Teams:", hero_rusty_man.teams)
print("Spider-Boy:", hero_spider_boy)
print("Spider-Boy Teams:", hero_spider_boy.teams)
这个函数展示了如何:
- 创建团队和英雄实例
- 通过列表直接建立多对多关联
- 使用会话管理数据库操作
- 提交事务并刷新对象状态
- 验证关联关系是否正确建立
实际应用建议
- 索引优化:模型中的索引设置合理,name和age字段都有索引,适合查询场景
- 事务管理:使用上下文管理器(with语句)确保会话正确关闭
- 数据验证:在实际应用中,可以添加更多的数据验证逻辑
- 性能考虑:对于大型数据集,可能需要考虑批量插入优化
总结
通过SQLModel实现多对多关系非常直观,主要步骤包括:
- 定义中间表模型
- 在主模型中通过Relationship建立关联
- 指定link_model指向中间表
- 使用back_populates建立双向关系
这种设计既保持了代码的简洁性,又提供了强大的关系管理能力,是处理复杂数据关联的理想选择。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考