Imported from myuanz/dataclassql (
AGENTS.md). Install upstream withnpx skills add myuanz/dataclassql. Copyright stays with the author.
AGENTS
项目概览
- DataclassQL 是一个围绕“纯 dataclass 定义”构建的 ORM 客户端生成器, 目标是成为 Prisma for Python 的精神继任者。
- 通过分析模型 dataclass, 自动生成带完整类型提示的客户端及表访问层, 以获得静态类型检查能力 (pyright/mypy) 与更直观的开发体验。
- 目前聚焦 SQLite, 已实现代码生成、数据库 schema 推送、运行时 CRUD 与包含机制, 并提供懒加载的关系解析。
技术栈与依赖
- 要求 Python ≥ 3.12。
- 核心依赖:
jinja2(模板渲染)、pypika(SQL 构造与 schema 操作)、typing_extensions(Typing 支持)。 - 使用
uv管理环境与指令, 例如:uv add dclassql,uv run pytest .,uv run pyright .。
目录速览
src/dclassql/codegen.py+templates/client_module.py.jinja: 负责生成客户端模块代码。src/dclassql/model_inspector/: 其下有relationships.py负责关系表达式解析table_constraints.py负责单表约束graph.py负责表间关系图编排type_hints.py: 将字段类型统一解析为TypeHint,
src/dclassql/push/: 数据库 schema 推送逻辑, 当前实现SQLitePusher。src/dclassql/runtime/: 运行时后端、懒加载关系、数据源解析与 sqlite 适配器。src/dclassql/cli.py:dclassql命令入口, 提供generate/push-db子命令。tests/: 覆盖 CLI、代码生成、schema 推送、运行时、类型检查等场景。
代码生成流水线
ModelGraph.from_models(models)统一解析 dataclass、数据源、列、索引、外键和关系;ClientCompiler消费ModelGraph, 构造渲染上下文并生成GeneratedModule_TypeRenderer负责把 Python 类型对象转成字符串表示, 处理Annotated、UnionType等复杂类型。- 模板渲染产物包括:
Client类: 继承运行时ClientBase, 维护默认数据源并初始化每个类型化表属性和_tables。*Table类: 封装insert/insert_many/find_many/find_first等方法, 依赖运行时后端。*Insertdataclass、*InsertDict/*WhereDict/*IncludeDict/*OrderByDictTypedDict, 以及T*IncludeCol/T*SortableColLiteral 类型别名。ColumnSpec、TableRelation等元信息用于运行时懒加载和 schema 推送
- 生成代码默认写入 model 同目录的
*_client包, 也可通过--target package写入安装包。
数据模型解析
model_inspector.ModelGraph:- 使用
get_type_hints、fields等 API 解析 dataclass 字段, 并立即转换为TypeHint。 - 按字段名索引的分析数据使用
FieldTo[T], 常用的字段类型与候选连接分别为FieldToTypeHint和FieldToLink。 Link自带 source、attribute、target 和 cardinality;foreign_key()将本地 Link、可选 backref Link 与列 mapping 组合为Relationship。- 借助
model_inspector.table_constraints.TableConstraints的 fake self 机制处理primary_key/index/unique_index方法返回的ColGroup。
- 使用
- 主键约定:
- 未声明
primary_key()时默认主键名为id; 如果 dataclass 没有id字段, schema 会创建后端对应的隐式自增id主键列。 - 隐式
id会出现在Insert/InsertDict/WhereDict/OrderByDict/ upsert where 等间接描述里, 查询和写入返回值仍保持原 dataclass 字段, 不给返回对象补id。
- 未声明
- 关系与外键:
RelationAttribute/RelationProxy/ForeignKeyComparison支持self.user.id == self.user_id等表达式, 映射为关系和键约束。foreign_key()在替换模型 globals/closure 的代理环境中执行, 不修改原始 dataclass。
运行时与数据库推送
push.db_push:- 接收已生成客户端中的
SchemaTableProtocol元信息, 按 provider 委派给对应的DatabasePusher - 默认注册
SQLitePusher, 支持外部register_pusher扩展。
- 接收已生成客户端中的
push/sqlite.py:_infer_sqlite_type将 Python 类型映射为 SQLite 列类型, 处理Annotated/Union。SQLiteSchemaBuilder生成CREATE TABLE语句、索引定义, 支持自增主键内联定义。SQLitePusher能检查现有 schema, 根据SchemaDiff判定是否需要重建表; 重建时会创建临时表迁移数据, 并可通过confirm_rebuild回调确认。sync_indexes=True会删除多余索引/重建缺失索引。
runtime/backends.base.BackendBase:- 定义通用 CRUD 实现, 支持 typed insert payload (dataclass、TypedDict、Mapping)。
- delete/delete_many、update/update_many、insert/insert_many、find_first/find_many
runtime/backends.sqlite.SQLiteBackend:- 基于 sqlite3, 可接受连接或线程局部工厂, 实现批量插入、
query_raw/execute_raw。 - 默认使用
sqlite3.Row作为 row factory, 保证列名访问。
- 基于 sqlite3, 可接受连接或线程局部工厂, 实现批量插入、
runtime/backends.lazy:- 定义
_LazyRelationDescriptor、单值 lazy proxy、LazyRelationView与eager()。 LazyRelationState保存实例关系的查询绑定; identity weak registry 只登记未 include 的 lazy 关系, 不依赖模型的__hash__/__eq__。
- 定义
runtime/datasource.py+sqlite_adapters.py: 解析sqlite:///...URL, 注册日期/时间适配器, 构造连接。runtime/client_base.py:ClientBase统一实现 backend、连接缓存、push_db()与关闭逻辑, 生成 Client 只提供数据源和表集合。
CLI 与工具链
- 安装后提供
dclassql命令 (在pyproject.toml注册)。 dclassql -m model.py generate:- 载入模型模块 (
importlib.util), 收集 dataclass, 生成 typed 代码。 - 写入生成客户端
- 可传
--push-db在生成后立即使用新客户端推送 schema。
- 载入模型模块 (
dclassql -m model.py push-db:- 载入已有生成客户端并调用其
push_db, 因此要求先执行generate。
- 载入已有生成客户端并调用其
- CLI 出错直接抛异常; 统一由
main()捕获并写至 stderr。 - src/dclassql/client.py 是生成处理的文件, 不要试图直接编辑. 执行 pytest 后就会重建
辅助模块与工具
db_pool.BaseDBPool+save_local装饰器: 在线程局部缓存数据库连接/对象, 提供close_all()释放资源。typing.py定义生成器使用的泛型 TypeVar (ModelT,InsertT等)。unwarp.py提供unwarp_or/unwarp_or_raise等辅助函数, 在生成代码中用于处理可空值。
测试与质量保障
- 使用
pytest(见tests/) 覆盖:test_codegen*: 校验生成代码结构、上下文数据、导出内容。test_sqlite_push.py: 验证 schema/索引生成与重建逻辑、diff 报告。test_runtime_sqlite.py: 集成测试 CRUD、懒加载、线程安全、批量插入与错误分支。test_cli.py: 检查dclassql命令输出、生成文件与 push-db 功能。test_typecheck.py: 调用uv run pyright验证类型错误能被检测。- 本项目不大, 每次应当使用全量的 pytest, 避免部分测试
tests/results.py存放期望的生成代码快照 (持续更新)。- 使用
uv run pyright .检查项目类型, 项目不大, 不用特地只检查单独的文件, 每次都检查项目库 - 完成一个功能点后, 更新 TARGET.md 中的路线图
- 尽量少用 getattr, 本项目有静态分析阶段, 很多东西明确是已知的.
- 少在 ModelRenderContext 新增字段, 能在jinja里写的渲染逻辑直接在jinja实现, 能复用已有字段的尽量复用
- 提交信息沿用当前 Conventional Commits 风格, 例如
feat: ...、fix: ...、docs: ...、refactor: ...。
代码风格
- 少多 Any 和 cast, 多用泛型, 特别是3.12+的泛型语法
def f[T](x: T, y: T) -> T: ... - 要有大局观, 尽量少重复实现
设计
- 检查 @TARGET.md
当前能力边界与路线
- 仅支持 SQLite; 其他数据库需实现自定义
DatabasePusher与运行时 Backend。 - 查询 API 聚焦基本 CRUD, 未来计划扩展更复杂的查询能力和更多数据库驱动 (见
TARGET.md路线图)。 - 外键在数据库层面仍为“虚拟外键”(不创建真实约束), 依赖运行时约定。
- 包含机制目前基于懒加载与
includebool map, 不支持复杂嵌套查询条件。