python pony
# Python Pony一个被低估的ORM其实比你想的更好用说到Python的ORM大部分人第一个想到的是SQLAlchemy其次是Django ORM。如果你去问十个Python开发者可能有七八个会提到这两个。Pony ORM也许有人听说过但真正用过的不会太多。这有点可惜因为Pony在某些场景下其实比那些“大牌”更好用。它到底是什么东西Pony ORM是一个Python的关系对象映射库简单说就是把数据库的表结构映射成Python的类让开发者用Python对象的方式操作数据库而不是写一堆SQL字符串。但它跟其他ORM有个挺大的区别——它用一种叫“实体关系图”的方式描述数据库结构代码写起来更像是在画一张ER图。举个例子如果你用SQLAlchemy定义一个用户表大概是这样classUser(Base):__tablename__usersidColumn(Integer,primary_keyTrue)nameColumn(String)而Pony的写法是classUser(db.Entity):idPrimaryKey(int,autoTrue)nameRequired(str)看着差别不大但如果你定义有外键关联的表区别就出来了。Pony允许你在两个实体之间直接写关联属性像这样classUser(db.Entity):idPrimaryKey(int,autoTrue)nameRequired(str)postsSet(Post)classPost(db.Entity):idPrimaryKey(int,autoTrue)titleRequired(str)authorRequired(User)这其实就是在画ER图——用户和帖子之间是一对多关系用户有帖子集合帖子属于某个用户。Pony会自动处理外键你不用手动定义那个外键字段。它能解决什么实际问题1. 查询语法极其自然这是Pony最大的亮点。它的查询语法很像Python的列表推导式读起来基本就是自然语言。比如你要查所有标题包含“Python”并且发布时间在一周内的帖子用Pony写select(pforpinPostifPythoninp.titleandp.pub_dateweek_ago)对比SQLAlchemysession.query(Post).filter(Post.title.contains(Python),Post.pub_dateweek_ago)Pony的写法几乎和你在纸上画的条件筛选一模一样。如果你需要按作者名排序再加一个order_by子句它依然保持这种“说人话”的风格。2. 懒加载与急加载的自动管理很多ORM在懒加载上面做得不够聪明。比如你加载一个用户列表然后循环中访问每个用户的posts如果默认是懒加载就会产生N1查询问题如果急加载又可能加载了你根本用不上的数据。Pony在这里有个挺聪明的处理方式。它会在单个查询上下文中延迟加载但如果你在循环中访问关联数据它会自动意识到你可能需要所有数据只发一次额外查询。这点是我实际使用中觉得最舒服的地方不用刻意去调优加载策略。3. 数据库迁移不需要额外工具Django有migrationsSQLAlchemy有Alembic。Pony的做法比较特别——它会在启动时自动检测实体定义和数据库实际结构之间的差异然后自动执行ALTER TABLE。当然你可以关掉这个功能但对于开发阶段来说这真的很方便。改完模型定义重启应用表结构就同步了。生产环境你可能想手动控制但开发迭代时少了个需要操心的事情。怎么上手用起来安装就是一条命令pipinstallpony然后你需要连接数据库、定义实体、操作数据。一个完整的最小例子frompony.ormimport*# 创建数据库对象dbDatabase()# 定义实体classUser(db.Entity):idPrimaryKey(int,autoTrue)nameRequired(str,uniqueTrue)ageOptional(int)postsSet(Post)classPost(db.Entity):idPrimaryKey(int,autoTrue)titleRequired(str)contentRequired(str)authorRequired(User)# 绑定数据库db.bind(providersqlite,filename:memory:)# 生成表结构db.generate_mapping(create_tablesTrue)# 操作数据注意db_session上下文db_sessiondefadd_user_and_posts():userUser(name张三,age25)Post(title第一篇文章,content这是内容,authoruser)Post(title第二篇文章,content这也是内容,authoruser)db_sessiondefquery_posts():postsselect(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的查询返回的是一个查询对象不是列表。当你遍历它时它实际上是一边迭代一边从数据库取数据类似于生成器。但如果你需要取全部数据然后做随机访问最好显式做切片resultselect(pforpinPost)[:100]这样只取前100条而不是把所有记录加载到内存。3. 复杂关联查询时的陷阱Pony的关联查询写起来很自然但某些情况下可能生成不够优化的SQL。比如你需要跨多个表做聚合查询它的查询计划器有时候会生成子查询而非JOIN。这种情况不常见但如果你发现查询响应慢了建议打开Pony的日志功能看看它实际生成的SQL是什么set_sql_debug(True)这条语句会打印所有执行的SQL调试性能问题时特别有用。跟其他ORM比怎么样vs SQLAlchemySQLAlchemy是那种什么都能干的全能型选手支持几十种数据库有成熟的事件系统、自定义类型、声明式扩展。Pony在这些方面差很多——它支持的数据库只有常用的几种SQLite, PostgreSQL, MySQL, Oracle, CockroachDB扩展性也不如SQLAlchemy。但Pony在查询可读性和易用性上明显胜出。SQLAlchemy的查询语法虽然强大但写多了会发现它越来越接近SQL而不是Python。Pony保持了纯Python风格的查询代码审查时更容易理解业务逻辑。还有一个差异是Pony允许在实体定义中直接写约束条件比如classUser(db.Entity):nameRequired(str,uniqueTrue)ageRequired(int,min0,max150)这种字段级别的验证在Pony里是内置的写入数据时会自动校验不用额外写表单验证。vs Django ORMDjango ORM是为Django框架定身的和框架本身深度绑定。如果你不是用Django基本没法用它的ORM当然也可以硬拆出来但很麻烦。Pony是独立的库Flask、Tornado、FastAPI都可以用。Django ORM在复杂查询方面比较弱比如嵌套子查询、窗口函数这些支持得不好。Pony在这方面虽然不如SQLAlchemy但比Django ORM好不少。不过Django ORM有一个东西是Pony没有的——QuerySet的链式调用和延迟执行。Pony的查询一旦被迭代数据就取出来了不能像Django那样一直链式调用filter、exclude最后才求值。vs Tortoise ORMTortoise是异步原生的ORM专为asyncio设计。Pony是同步的虽然它也有异步尝试但目前还不成熟。如果你写的是异步Web框架比如FastAPI或QuartTortoise可能会更顺滑。Pony在异步场景下需要自己维护线程池不太优雅。说句实在话Pony不是那种“一招鲜”的ORM它有明显的短处——支持数据库少、社区规模小、没有成熟的迁移管理工具、异步支持不完善。但如果你在做一个中等规模的项目团队里Python水平参差不齐Pony的简单和直观反而能让新人快速上手。特别是那些写业务逻辑的同事他们不用深入了解ORM的内部机制就能写出可读性很强的查询。我个人觉得Pony最合适的场景是快速原型开发和中小型内部系统。大型系统或者对性能有极端要求的地方还是让SQLAlchemy上场吧。但如果你厌倦了反复调整query、写一堆lambda表达式、调试懒加载问题不妨试试Pony它的查询写起来真的很舒服。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2579438.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!