当前位置: 首页 > news >正文

python tortoise-orm

# Python Pony:一个被低估的ORM,其实比你想的更好用

说到Python的ORM,大部分人第一个想到的是SQLAlchemy,其次是Django ORM。如果你去问十个Python开发者,可能有七八个会提到这两个。Pony ORM?也许有人听说过,但真正用过的不会太多。这有点可惜,因为Pony在某些场景下,其实比那些“大牌”更好用。

它到底是什么东西

Pony ORM是一个Python的关系对象映射库,简单说就是把数据库的表结构映射成Python的类,让开发者用Python对象的方式操作数据库,而不是写一堆SQL字符串。但它跟其他ORM有个挺大的区别——它用一种叫“实体关系图”的方式描述数据库结构,代码写起来更像是在画一张ER图。

举个例子,如果你用SQLAlchemy定义一个用户表,大概是这样:

classUser(Base):__tablename__='users'id=Column(Integer,primary_key=True)name=Column(String)

而Pony的写法是:

classUser(db.Entity):id=PrimaryKey(int,auto=True)name=Required(str)

看着差别不大?但如果你定义有外键关联的表,区别就出来了。Pony允许你在两个实体之间直接写关联属性,像这样:

classUser(db.Entity):id=PrimaryKey(int,auto=True)name=Required(str)posts=Set('Post')classPost(db.Entity):id=PrimaryKey(int,auto=True)title=Required(str)author=Required(User)

这其实就是在画ER图——用户和帖子之间是一对多关系,用户有帖子集合,帖子属于某个用户。Pony会自动处理外键,你不用手动定义那个外键字段。

它能解决什么实际问题

1. 查询语法极其自然

这是Pony最大的亮点。它的查询语法很像Python的列表推导式,读起来基本就是自然语言。

比如你要查所有标题包含“Python”并且发布时间在一周内的帖子,用Pony写:

select(pforpinPostif'Python'inp.titleandp.pub_date>week_ago)

对比SQLAlchemy:

session.query(Post).filter(Post.title.contains('Python'),Post.pub_date>week_ago)

Pony的写法几乎和你在纸上画的条件筛选一模一样。如果你需要按作者名排序,再加一个order_by子句,它依然保持这种“说人话”的风格。

2. 懒加载与急加载的自动管理

很多ORM在懒加载上面做得不够聪明。比如你加载一个用户列表,然后循环中访问每个用户的posts,如果默认是懒加载,就会产生N+1查询问题;如果急加载,又可能加载了你根本用不上的数据。

Pony在这里有个挺聪明的处理方式。它会在单个查询上下文中延迟加载,但如果你在循环中访问关联数据,它会自动意识到你可能需要所有数据,只发一次额外查询。这点是我实际使用中觉得最舒服的地方,不用刻意去调优加载策略。

3. 数据库迁移不需要额外工具

Django有migrations,SQLAlchemy有Alembic。Pony的做法比较特别——它会在启动时自动检测实体定义和数据库实际结构之间的差异,然后自动执行ALTER TABLE。当然,你可以关掉这个功能,但对于开发阶段来说,这真的很方便。改完模型定义,重启应用,表结构就同步了。生产环境你可能想手动控制,但开发迭代时少了个需要操心的事情。

怎么上手用起来

安装就是一条命令:

pipinstallpony

然后你需要连接数据库、定义实体、操作数据。一个完整的最小例子:

frompony.ormimport*# 创建数据库对象db=Database()# 定义实体classUser(db.Entity):id=PrimaryKey(int,auto=True)name=Required(str,unique=True)age=Optional(int)posts=Set('Post')classPost(db.Entity):id=PrimaryKey(int,auto=True)title=Required(str)content=Required(str)author=Required(User)# 绑定数据库db.bind(provider='sqlite',filename=':memory:')# 生成表结构db.generate_mapping(create_tables=True)# 操作数据,注意db_session上下文@db_sessiondefadd_user_and_posts():user=User(name='张三',age=25)Post(title='第一篇文章',content='这是内容',author=user)Post(title='第二篇文章',content='这也是内容',author=user)@db_sessiondefquery_posts():posts=select(pforpinPostifp.author.name=='张三')forpostinposts:print(post.title,post.content)

注意到@db_session了吗?这是Pony的上下文管理器,所有数据库操作必须在这个装饰器或上下文内进行。它负责管理事务、连接池和缓存。如果你忘记用这个装饰器,Pony会报错,提醒得很及时。

一些值得注意的实践

1. 把db_session放在视图层或业务层入口

不要在单个函数内部使用多个db_session,尽量让一个请求或一个任务共享一个session。Pony的session带有一个一级缓存,同一个实体对象在一次session内只加载一次,多次访问同一个记录不会重复查询数据库。如果你一个请求内切分了多个session,这个缓存效果就没了。

2. 处理大数据量时注意使用切片

Pony的查询返回的是一个查询对象,不是列表。当你遍历它时,它实际上是一边迭代一边从数据库取数据,类似于生成器。但如果你需要取全部数据然后做随机访问,最好显式做切片:

result=select(pforpinPost)[:100]

这样只取前100条,而不是把所有记录加载到内存。

3. 复杂关联查询时的陷阱

Pony的关联查询写起来很自然,但某些情况下可能生成不够优化的SQL。比如你需要跨多个表做聚合查询,它的查询计划器有时候会生成子查询而非JOIN。这种情况不常见,但如果你发现查询响应慢了,建议打开Pony的日志功能,看看它实际生成的SQL是什么:

set_sql_debug(True)

这条语句会打印所有执行的SQL,调试性能问题时特别有用。

跟其他ORM比怎么样

vs SQLAlchemy

SQLAlchemy是那种什么都能干的全能型选手,支持几十种数据库,有成熟的事件系统、自定义类型、声明式扩展。Pony在这些方面差很多——它支持的数据库只有常用的几种(SQLite, PostgreSQL, MySQL, Oracle, CockroachDB),扩展性也不如SQLAlchemy。

但Pony在查询可读性和易用性上明显胜出。SQLAlchemy的查询语法虽然强大,但写多了会发现它越来越接近SQL,而不是Python。Pony保持了纯Python风格的查询,代码审查时更容易理解业务逻辑。

还有一个差异是Pony允许在实体定义中直接写约束条件,比如:

classUser(db.Entity):name=Required(str,unique=True)age=Required(int,min=0,max=150)

这种字段级别的验证在Pony里是内置的,写入数据时会自动校验,不用额外写表单验证。

vs Django ORM

Django ORM是为Django框架定身的,和框架本身深度绑定。如果你不是用Django,基本没法用它的ORM(当然也可以硬拆出来,但很麻烦)。Pony是独立的库,Flask、Tornado、FastAPI都可以用。

Django ORM在复杂查询方面比较弱,比如嵌套子查询、窗口函数这些,支持得不好。Pony在这方面虽然不如SQLAlchemy,但比Django ORM好不少。

不过Django ORM有一个东西是Pony没有的——QuerySet的链式调用和延迟执行。Pony的查询一旦被迭代,数据就取出来了,不能像Django那样一直链式调用filter、exclude,最后才求值。

vs Tortoise ORM

Tortoise是异步原生的ORM,专为asyncio设计。Pony是同步的(虽然它也有异步尝试,但目前还不成熟)。如果你写的是异步Web框架,比如FastAPI或Quart,Tortoise可能会更顺滑。Pony在异步场景下需要自己维护线程池,不太优雅。

说句实在话

Pony不是那种“一招鲜”的ORM,它有明显的短处——支持数据库少、社区规模小、没有成熟的迁移管理工具、异步支持不完善。但如果你在做一个中等规模的项目,团队里Python水平参差不齐,Pony的# # 深入聊聊 Python 里的 Tortoise-ORM

之前有个朋友问我,他写了个异步的 Web 服务,想找个好用的 ORM,但又不想碰那些重量级的东西。我当时就想到了 Tortoise-ORM。这个库说实话,在 Python 的 ORM 生态里不算最出名的那一批,但用下来你会发现它在某些场景下真的很顺手。

它到底是什么

Tortoise-ORM 是一个异步的 ORM 框架,专门为 Python 的 asyncio 生态设计的。你可能用过 SQLAlchemy,但那个主要是同步的,虽然在 async 方面做了很多工作,但总觉得有点 “后天改造” 的味道。Tortoise-ORM 从出生就是异步的,底层的数据库连接全部走 async/await 这套。

它借鉴了很多 Django ORM 的设计思路。比如模型的声明方式、查询的 API,如果你写过 Django,上手 Tortoise-ORM 会感觉特别亲切。但又有自己的特色,比如它支持数据库迁移、内置了 Aerich 这个迁移工具、还支持多种数据库后端。

不过有一点要注意,它不像 SQLAlchemy 那样可以处理特别复杂的 SQL 查询。它的定位是够用就好,在常见场景下让你写得快、跑得快。

能帮我们解决什么问题

最直观的是,如果你在写 FastAPI、Sanic、Quart 这类异步框架,Tortoise-ORM 能让你避免线程切换带来的麻烦。同步 ORM 在异步环境下用起来总是要小心各种阻塞,绕来绕去。

它的模型定义方式让数据表关系变得很容易管理。比如你有用户和文章,用户能发表多篇文章,用 ForeignKeyField 一申明就行。查用户的所有文章就是一个 await user.articles.all() 的事。这种链式查询在写业务逻辑时特别自然。

还有一个很实用的是它内置了 Pydantic 模型生成功能。你可以直接从 Tortoise 模型生成 Pydantic 模型,用于 API 的请求校验和响应序列化。省去了很多手动写序列化器的时间。

实际用起来感觉怎么样

拿一个简单的博客系统举个例子。先定义模型:

fromtortoiseimportModel,fieldsclassUser(Model):id=fields.IntField(pk=True)username=fields.CharField(max_length=32,unique=True)email=fields.CharField(max_length=128)articles=fields.ReverseRelation["Article"]classArticle(Model):id=fields.IntField(pk=True)title=fields.CharField(max_length=255)content=fields.TextField()author=fields.ForeignKeyField("models.User",related_name="articles")created_at=fields.DatetimeField(auto_now_add=True)

这里有几个有意思的设计。ForeignKeyField 里的 “models.User” 这种字符串引用方式,在模型有循环依赖时特别有用。related_name 让你可以反过来从用户查文章。

初始化数据库连接一般在应用启动时做:

fromtortoiseimportTortoiseasyncdefinit_db():awaitTortoise.init(db_url="sqlite://db.sqlite3",modules={"models":["path.to.models"]})# 生成表结构(开发环境用)awaitTortoise.generate_schemas()

查询的写法很直观。比如获取最近的 10 篇文章,同时预加载作者信息:

articles=awaitArticle.all().select_related("author").order_by("-created_at").limit(10)

这里 select_related 的作用是减少数据库查询次数,避免 N+1 问题。Tortoise 还提供了 prefetch_related,在查询多对多关系时很有用。

一些实践中总结的经验

用 Tortoise-ORM 一段时间后,我总结了几点值得注意的地方。

事务的处理需要小心。Tortoise 的事务是基于连接隔离的,你要确保事务内的所有操作都用同一个数据库连接:

asyncwithin_transaction()asconn:user=awaitUser.create(username="test",using_db=conn)article=awaitArticle.create(title="test",author=user,using_db=conn)

还有一个坑是关于延迟加载的。如果你在循环里用 await 去取关联数据,可能会产生很多次数据库查询。比如这样:

articles=awaitArticle.all()forarticleinarticles:author=awaitarticle.author# 每次循环都查一次数据库

更好的做法是用 select_related 或者 prefetch_related 一次性查出来。

在数据模型的设计上,建议把业务逻辑放在模型的方法里。比如用户注册时密码的处理,可以写一个类方法:

classUser(Model):# ... 字段定义 ...@classmethodasyncdefregister(cls,username:str,password:str)->"User":hashed=hash_password(password)# 假设有个hash函数returnawaitcls.create(username=username,password_hash=hashed)

这样调用方只需要 User.register(username, password),里面怎么存密码是模型自己的事。

和其他 ORM 的比较

和 SQLAlchemy 相比,Tortoise-ORM 在异步支持上更原生。SQLAlchemy 虽然有 async 模式,但用起来总觉得有层隔膜。不过如果你的查询特别复杂,需要各种子查询、CTE、窗口函数,SQLAlchemy 会更有优势。

和 Django ORM 相比,Tortoise-ORM 轻量化很多。Django ORM 绑定在 Django 框架里,而 Tortoise 可以独立使用。但 Django ORM 的生态更加成熟,文档更全,遇到的坑也基本都有前人的解决方案。

和 GINO 相比,GINO 更简洁但功能偏少。Tortoise-ORM 在模型关联、迁移支持上做得更好。不过如果你只需要非常基础的数据库操作,GINO 的学习成本更低。

总的来说,Tortoise-ORM 特别适合中小型项目,尤其是那些基于 asyncio 的 Web 服务。它让你在很多常见场景下写得很舒服,对于不常见的复杂需求,你也可以通过执行原生 SQL 来兜底。毕竟,OR 映射层再强大,底层还是 SQL。简单和直观反而能让新人快速上手。特别是那些写业务逻辑的同事,他们不用深入了解ORM的内部机制,就能写出可读性很强的查询。

我个人觉得,Pony最合适的场景是快速原型开发和中小型内部系统。大型系统或者对性能有极端要求的地方,还是让SQLAlchemy上场吧。但如果你厌倦了反复调整query、写一堆lambda表达式、调试懒加载问题,不妨试试Pony,它的查询写起来真的很舒服。

http://www.jsqmd.com/news/746708/

相关文章:

  • 一次模型路由误触发引发的成本雪崩:从额度超限到动态降级的工程复盘
  • 英语阅读_Fashion Fusion Camp
  • 对比与选型利用Taotoken模型广场为你的应用找到最合适的大模型
  • C# Winform项目日志管理:除了NLog,你真的会看日志文件吗?(含日志分析与问题排查实战)
  • 5分钟解放你的游戏时间:三月七小助手完全指南
  • 如何快速下载GitHub文件和目录:DownGit完整指南
  • Taotoken 用量看板如何帮助团队管理大模型 API 成本
  • D03 注意力机制手算与代码实现
  • 半桥 vs 全桥,全波 vs 全桥:LLC谐振变换器拓扑选型实战避坑指南
  • 在Nodejs后端服务中集成Taotoken实现异步AI对话功能
  • Prompt4ReasoningPapers:大模型推理增强技术知识图谱与实战指南
  • OpenMMLab全家桶(mmdet/mmcv)保姆级安装指南:从MIM一键安装到源码编译避坑
  • Higress安装后必做的5件事:从Console初始化到生产就绪检查清单
  • 一文读懂 Graphify 知识图谱
  • PvZWidescreen技术解析:用Rust重绘经典游戏的宽屏体验
  • 神经网络学习模加法的机制与可解释性研究
  • 利用 Taotoken 实现多模型 API 密钥的统一管理与访问控制
  • 如何通过Fast-GitHub插件实现GitHub下载速度10倍提升的终极指南
  • 从MATLAB代码入手:手把手教你复现OTFS调制解调核心模块(附完整函数解析)
  • 从一次CI/CD构建失败说起:深入理解package.json中版本锁定的利与弊
  • 隐性人工智能驯化机制的实证研究.一份基于自我民族志、参与式行动研究与活体实验室方法的混合范式论文
  • 从零开始:用普通PC轻松打造macOS系统的最佳实践指南
  • 创业公司如何利用 Taotoken 管理多个 AI 模型的调用成本
  • 机器人记忆与策略理解:关键技术突破与应用实践
  • 如何快速掌握TouchGal:从零开始的完整Galgame社区实战指南
  • MR微观因果推断分析
  • 2026年4月市场热门的钢结构源头厂家推荐,头部钢结构供应商找哪家,耐候性好的钢结构,适应不同气候 - 品牌推荐师
  • 从零掌握提示工程:系统化学习与AI高效对话的核心技艺
  • §03 增补|驯化机制 D7-D10 扩展模式 v1.0基于 2026-05-02 实证案例·补全后6类→10类完整驯化谱系
  • Ofd2Pdf完整指南:如何快速免费将OFD转换为PDF