尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

SQLModel 教程:为关联表创建行数据——外键列、自动刷新与连接团队和英雄

发布时间:2026/9/21 7:36:18

资讯中心
01
ARTICLE

SQLModel 教程:为关联表创建行数据——外键列、自动刷新与连接团队和英雄

SQLModel 教程:为关联表创建行数据——外键列、自动刷新与连接团队和英雄
SQLModel 教程为关联表创建行数据——外键列、自动刷新与连接团队和英雄【免费下载链接】sqlmodelSQL databases in Python, designed for simplicity, compatibility, and robustness.项目地址: https://gitcode.com/gh_mirrors/sq/sqlmodel本指南基于 SQLModel 官方教程「Create and Connect Rows」章节讲解如何通过外键列foreign key column在两个已连接的数据库表team与hero中创建行数据并用外键把「英雄」关联到「团队」。读完本文你将掌握如何在同一个 Session 中先创建并提交团队、如何通过已提交对象的id字段拿到数据库自动生成的主键、如何用team_id把英雄连接到团队、以及为什么访问刚提交对象的属性会触发一次隐式的数据库刷新。本文对应的完整示例代码位于 docs_src/tutorial/connect/insert/tutorial001_py310.py。本章目标两张表的最终数据上一章我们已经通过foreign_keyteam.id建好了team与hero两张互相连接的表见 创建关联表教程。本章将向两张表写入行数据。完成本章所有代码后team表将包含两行idnameheadquarters1PreventersSharp Tower2Z-ForceSister Margarets Bar而hero表将包含三行其中team_id列指向对应的团队idnamesecret_nameageteam_id1DeadpondDive Wilsonnull22Rusty-ManTommy Sharp4813Spider-BoyPedro Parqueadornullnull也就是说Deadpond 属于 Z-ForceRusty-Man 属于 PreventersSpider-Boy 暂时没有团队。每行hero通过team_id指向一行team注意本章不会把 Spider-Boy 加入 Preventers——那是后续「更新数据连接」章节的内容见 Update Data Connections。起点上一章遗留的代码我们继续使用上一章的代码作为起点在其上追加新内容。基础部分模型定义、引擎、建表函数如下from sqlmodel import Field, SQLModel, create_engine class Team(SQLModel, tableTrue): id: int | None Field(defaultNone, primary_keyTrue) name: str Field(indexTrue) headquarters: str class Hero(SQLModel, tableTrue): id: int | None Field(defaultNone, primary_keyTrue) name: str Field(indexTrue) secret_name: str age: int | None Field(defaultNone, indexTrue) team_id: int | None Field(defaultNone, foreign_keyteam.id) sqlite_file_name database.db sqlite_url fsqlite:///{sqlite_file_name} engine create_engine(sqlite_url, echoTrue) def create_db_and_tables(): SQLModel.metadata.create_all(engine) def main(): create_db_and_tables() if __name__ __main__: main()关键点在于Hero.team_id这一列Field(defaultNone, foreign_keyteam.id)告诉数据库hero.team_id的值必须对应team.id中某一行的值这就是两张表连接的桥梁。完整代码见 docs_src/tutorial/connect/create_tables/tutorial001_py310.py。运行提示为了保证与文中输出一致运行示例前请先删除database.db文件从零开始创建数据库。用 SQLModel 为团队创建行现在定义一个create_heroes()函数把团队的创建也放进来。首先创建两支团队def create_heroes(): with Session(engine) as session: team_preventers Team(namePreventers, headquartersSharp Tower) team_z_force Team(nameZ-Force, headquartersSister Margarets Bar) session.add(team_preventers) session.add(team_z_force) session.commit()这段代码的模式你应该已经熟悉用前面创建的同一个engine在with块中开启一个sessionwith块结束后会自动关闭会话实例化两个Team模型对象把对象add到 session 中commit提交会话把变更持久化到数据库。把函数挂到 main 上别忘了把create_heroes()加入main()这样从命令行运行程序时才会执行def main(): create_db_and_tables() create_heroes()运行并观察 SQL运行uv run python app.py控制台会输出省略了前面建表的日志// 自动开启事务 INFO Engine BEGIN (implicit) // 把团队写入数据库 INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?) INFO Engine [generated in 0.00050s] (Preventers, Sharp Tower) INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?) INFO Engine [cached since 0.002324s ago] (Z-Force, Sister Margarets Bar) INFO Engine COMMIT可以看到SQLModel 生成的正是标准的 SQLINSERT语句。之所以能打印这些 SQL是因为建引擎时传入了echoTrue[cached since ...]表示同一条语句已编译缓存直接复用参数执行。这也印证了 SQLModel 底层正是基于 SQLAlchemy 引擎与方言编译机制相关实现见 sqlmodel/ext/asyncio/session.py 与 sqlmodel/orm/session.py。在代码中为英雄创建行接下来创建第一个英雄对象。因为Hero模型现在有了team_id字段列、属性我们可以直接使用刚才创建的Team对象身上的id来赋值hero_deadpond Hero( nameDeadpond, secret_nameDive Wilson, team_idteam_z_force.id )虽然这个英雄还没有提交到数据库但这里已经有两件事值得注意如果数据库里本来就有团队我们根本无法预知数据库会给每支团队自动分配什么 ID——不能想当然地猜测是1或2但只要团队创建并提交成功我们就可以通过对象的id字段拿到真实 ID。这里引出了一个重要机制访问一个刚提交过的模型对象的属性例如team_z_force.id会自动触发一次从数据库刷新该对象数据的操作然后把字段值暴露出来。因此即使我们此刻还没有提交这个英雄仅仅因为使用了team_z_force.id就会向数据库发送 SQL 去取回这支团队的数据。这一行代码单独就会产生如下输出INFO Engine BEGIN (implicit) INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters FROM team WHERE team.id ? INFO Engine [generated in 0.00025s] (2,)这就是 SQLAlchemy 的「过期对象expired按需重载」机制commit 之后对象属性被标记为过期下次访问属性时会执行一次按主键的SELECT刷新。在 SQLModel 中你不需要手动做任何事它由 sqlmodel/orm/session.py 中封装的 Session 自动处理。创建更多英雄继续创建另外两个英雄hero_rusty_man Hero( nameRusty-Man, secret_nameTommy Sharp, age48, team_idteam_preventers.id, ) hero_spider_boy Hero(nameSpider-Boy, secret_namePedro Parqueador)创建hero_rusty_man时访问了team_preventers.id同样会触发一次刷新输出INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters FROM team WHERE team.id ? INFO Engine [cached since 0.001795s ago] (1,)这里还有一点值得注意team_id被标记为int | None意味着它在数据库里可以是NULL在 Python 中是None。也就是说英雄不一定要属于某个团队——本例中 Spider-Boy 就没有团队因此创建时完全不传team_id即可。提交英雄接下来把三个英雄对象加入 session 并提交session.add(hero_deadpond) session.add(hero_rusty_man) session.add(hero_spider_boy) session.commit()输出如下INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?) INFO Engine [generated in 0.00022s] (Deadpond, Dive Wilson, None, 2) INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?) INFO Engine [cached since 0.0007987s ago] (Rusty-Man, Tommy Sharp, 48, 1) INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?) INFO Engine [cached since 0.001095s ago] (Spider-Boy, Pedro Parqueador, None, None) INFO Engine COMMIT注意第三行Spider-Boy 的age和team_id都是None对应数据库中的NULL。刷新并打印英雄现在刷新这三个英雄对象并打印以便看到它们新获得的、指向团队的idsession.refresh(hero_deadpond) session.refresh(hero_rusty_man) session.refresh(hero_spider_boy) print(Created hero:, hero_deadpond) print(Created hero:, hero_rusty_man) print(Created hero:, hero_spider_boy)session.refresh()会强制从数据库重新加载对象的所有属性把数据库自动生成的主键等数据同步回 Python 对象。命令行输出$ uv run python app.py // 前面的输出省略 // 自动开启事务 INFO Engine BEGIN (implicit) // 刷新第一个英雄 INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id FROM hero WHERE hero.id ? INFO Engine [generated in 0.00021s] (1,) // 刷新第二个英雄 INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id FROM hero WHERE hero.id ? INFO Engine [cached since 0.001575s ago] (2,) // 刷新第三个英雄 INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id FROM hero WHERE hero.id ? INFO Engine [cached since 0.002518s ago] (3,) // 打印英雄 Created hero: id1 secret_nameDive Wilson team_id2 nameDeadpond ageNone Created hero: id2 secret_nameTommy Sharp team_id1 nameRusty-Man age48 Created hero: id3 secret_namePedro Parqueador team_idNone nameSpider-Boy ageNone三个英雄现在都带上了各自的team_idDeadpond → 2Z-ForceRusty-Man → 1PreventersSpider-Boy → None无团队。关系在 SQL 中的本质在 SQL 数据库中关系relationship本质上就是「一张表的某列引用另一张表某列的值」。我们这里正是这样处理的team_id只是hero表上的一个普通列字段——而这正是它在数据库中的真实形态。完整的本章代码见 docs_src/tutorial/connect/insert/tutorial001_py310.pyfrom sqlmodel import Field, Session, SQLModel, create_engine class Team(SQLModel, tableTrue): id: int | None Field(defaultNone, primary_keyTrue) name: str Field(indexTrue) headquarters: str class Hero(SQLModel, tableTrue): id: int | None Field(defaultNone, primary_keyTrue) name: str Field(indexTrue) secret_name: str age: int | None Field(defaultNone, indexTrue) team_id: int | None Field(defaultNone, foreign_keyteam.id) sqlite_file_name database.db sqlite_url fsqlite:///{sqlite_file_name} engine create_engine(sqlite_url, echoTrue) def create_db_and_tables(): SQLModel.metadata.create_all(engine) def create_heroes(): with Session(engine) as session: team_preventers Team(namePreventers, headquartersSharp Tower) team_z_force Team(nameZ-Force, headquartersSister Margarets Bar) session.add(team_preventers) session.add(team_z_force) session.commit() hero_deadpond Hero( nameDeadpond, secret_nameDive Wilson, team_idteam_z_force.id ) hero_rusty_man Hero( nameRusty-Man, secret_nameTommy Sharp, age48, team_idteam_preventers.id, ) hero_spider_boy Hero(nameSpider-Boy, secret_namePedro Parqueador) 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(Created hero:, hero_deadpond) print(Created hero:, hero_rusty_man) print(Created hero:, hero_spider_boy) def main(): create_db_and_tables() create_heroes() if __name__ __main__: main()仓库中的测试验证该示例在仓库中配有自动化测试tests/test_tutorial/test_connect/test_insert/test_tutorial001.py。测试通过importlib动态导入docs_src.tutorial.connect.insert.tutorial001_py310模块并把sqlite_url替换为内存数据库sqlite://、重建 engine避免污染真实的database.db然后调用module.main()最后断言三次print(Created hero:, ...)输出的字段完全符合预期Deadpondid1, ageNone, team_id2Rusty-Manid2, age48, team_id1Spider-Boyid3, ageNone, team_idNone。这与我们在命令行看到的输出完全一致也验证了「先创建并提交团队 → 读取其id→ 作为team_id创建英雄 → 提交 → 刷新」这条完整流程的正确性。总结与下一步本章展示了 SQLModel 中连接行的核心思路关系就是外键列team_id只是一个引用team.id的普通列创建行时直接赋值即可数据库生成的主键可通过对象属性读取访问刚提交对象的id会自动触发按主键的刷新查询可空外键int | None让「英雄没有团队」成为合法状态对应数据库的NULLSession 的标准流程add()→commit()→refresh()是创建关联行的固定套路。接下来你可以继续学习如何读取连接在一起的数据SELECT关联数据、JOIN与LEFT OUTER JOIN见 Read Connected Data、如何更新连接把 Spider-Boy 加入 Preventers见 Update Data Connections以及如何移除连接把team_id设回None见 Remove Data Connections。而在后续的Relationship Attributes章节中你还会学到更便捷的 Python 对象级关系操作入口见 docs/tutorial/relationship-attributes/index.md。✨【免费下载链接】sqlmodelSQL databases in Python, designed for simplicity, compatibility, and robustness.项目地址: https://gitcode.com/gh_mirrors/sq/sqlmodel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。