最近开源了一个我写了几年的后端元框架:UtilMeta ,它可以基于 Python 的类型注解标准高效开发 RESTful 接口和实现 CRUD (支持 Django ORM ),能够自动解析请求参数与生成 OpenAPI 文档,产出的代码简洁清晰,并且支持使用主流 Python 框架( Django, Flask, Fastapi, Starlette, Sanic, Tornado 等)作为运行时实现或渐进式整合
Github: https://github.com/utilmeta/utilmeta-py
欢迎大家来体验~(觉得项目不错也可以赏个 star 🥰) 有任何问题也可联系我。我的全网 ID 和微信号都是 voidZXL
1
tikazyq 282 天前
FastAPI 用 Pydantic 也可以生成 Redoc 类似 OpenAPI 文档的,感觉你这个框架更多的是为了兼容 Django 、Flask 之类的老项目吧?
|
2
metavoidx OP @tikazyq 感谢回复~ FastAPI 确实可以生成 OpenAPI 文档(这大概已经是现代 API 框架的标配了),但它自身对 RESTful CRUD 的能力支持有限,比较依赖生态,比如实现一个 Realworld 博客项目,UtilMeta 的实现( https://github.com/utilmeta/utilmeta-py-realworld-example-app )只需要 600+ 行代码,FastAPI 的实现( https://github.com/nsidnev/fastapi-realworld-example-app )需要 2500+ 行代码
所以我写 UtilMeta 一方面是方便现有的 Django, Flask 等项目进行平滑迁移,另一方面新项目也可以快速用它开发 RESTful 接口(目前全面支持 Django ORM 语法,后续也会支持其他主流 ORM 库),后面也会上线一个对应 API 管理平台( https://utilmeta.com/zh )实现 Python 后端项目的接口调试,日志查询等监控管理功能,所以会把 UtilMeta 做成了一个兼容 Python 主流框架的元框架 |
3
tangkikodo 278 天前
hi, 我是 pydantic-resolve 作者(也是那篇 composition oriented development pattern 的作者) , 在声明式描述数据这点上我很赞同你的观点, 我观点上的一些分歧在于是否应该和 ORM 关联的紧密,下面是我的解释。
首先,既然用申明方式来生成数据, 目的肯定是为了更加优雅的组合数据 一般来说, 后端生成一个前端直接可用的视图数据包括几个步骤。 1. 数据获取 2. 数据组装 (utilmeta 把 1 ,2 两个环节一次性解决了) 3. 面向业务进行转换 这么几个步骤。 (一般情况下 2 ,3 可能会混在一起) 我选择使用 dataloader 的原因是: 通过 ORM 获取关联数据的手段之一,这个行为如果用通用的描述来说的话, 类似于: def batch_query(keys, filters) 然后把查询结果绑定回每一条父记录。 这样在一对多查找的时候,既能使用外键, 还能指定额外过滤条件 https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/router/sample_2/readme-cn.md ( ORM relationship 上添加额外条件的查询写起来通常都会比较繁杂) 也就是说,loader 的内部实现可能是一段查询, 也可能是一个 rpc , 也可能是缓存查询 等等 这样做的好处是隔离了具体实现, 假如我要做服务拆分的话, 就能将封装的查询,转成一个 rpc 调用了。 关于 3 , 数据转换, 这也是直接从 ORM 拼装数据可能存在的一个小问题, 如果视图数据需要对视图数据的中间, 或者底层的数据做些转换, 聚合计算之类的功能, 很多时候就不得不拆开来遍历计算了。 pydantic-resolve 的思路是选择使用 post_method 来在每层提供一个 after resolved hook 来操作数据。 用来处理层级聚合, 字段转换会比较方便, 也符合声明式的风格。 https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/router/sample_4/readme-cn.md 说了一些拙见,框架本身是服务于特定目的, utilmeta 这样的申明式方法带来了很好的灵活性和使用便利。 而且被你详细的文档和丰富的 example 给震撼到了! 祝蒸蒸日上 |
4
metavoidx OP @tangkikodo 非常感谢支持,文档确实肝了挺长时间的哈哈~ 之前就在 Github 上看到过你的分享,印象深刻,我们优化和解决问题的大方向是一致的,不过我确实把 orm.Schema 类从设计上直接和一个 ORM model 进行绑定了,目前支持了 Django 的 model ,未来也会支持 SQLAlchemy 等其他主流 ORM 库的 model ,我其实是实现了一个 ORM adaptor ,把常用的查询操作进行抽象,然后提供给 orm.Schema 调用的
https://github.com/utilmeta/utilmeta-py/tree/main/utilmeta/core/orm/backends 这样确实没有办法完美覆盖 100% 的情况,但也足以简单轻松地应对 95% 的 CRUD 情况了,因为大部分用户也只是在一些主流的 ORM 模型上编写 RESTful 接口,只要把普通字段,关系对象(外键,多对,级联),表达式(计数/求和/平均等)这些常用的字段进行(带一定配置参数的)标准化实现就可以适用于绝大多数场景了,剩下的复杂场景可以一事一议,让用户自行在函数中编写查询逻辑组合拼接,因为任何框架都不可能处理所有问题,所以肯定要留出这样的自由度 “如果视图数据需要对视图数据的中间, 或者底层的数据做些转换, 聚合计算之类的功能”: 因为 orm.Schema 的方法调用的数据也并非一定是最终的 API 返回结果,用户可以把它作为一个期望明确的 “标准化” 数据源,通过声明返回自己需要的任意结构的合法数据,之后可以在函数中或者 Schema 类的 @property 属性中进行聚合计算和后处理工作 https://docs.utilmeta.com/py/zh/guide/schema-query/#property 因为我是尽可能希望用 Python 的原生语法和规范来定义声明式语法,尽量减少专有概念或语法的引入,不太希望规定太多的参数或者方法名,也是希望能减轻用户的学习成本和心智负担,一眼就能看明白框架代码的含义和作用 当然这也只是我设计哲学上的拙见 祝好~ 望多交流 |
5
tangkikodo 276 天前
@metavoidx
同意的, 我其实因为是个 sqlalchemy 苦手, 感觉配置 relationship 之类的太繁琐了,就刻意从这层脱身出来 囧 |