selfdb:用Python对象直接操控PostgreSQL的极简数据库层
> selfdb 是一个用 Python 对象直接映射 PostgreSQL 查询结果的极简数据库访问层,让数据库操作像操作普通 Python 对象一样自然。
## 痛点:ORM 之重与裸 SQL 之痛
在 Python 生态中,访问数据库主要有两条路:
1. **裸 SQL + 游标**:直接使用 `psycopg2` 或 `asyncpg` 执行 SQL 语句,然后手动处理返回的元组或字典。这种方式灵活、性能最好,但代码冗长且容易出错——你需要自己管理游标、处理类型转换、拼接查询条件,而且结果集是简单的 `dict` 或 `tuple`,无法获得 IDE 自动补全和类型提示。
2. **重型 ORM**:如 SQLAlchemy、Django ORM。它们提供了完整的对象关系映射、迁移工具、会话管理、关联加载等功能,但也带来了巨大的学习曲线和性能开销。很多场景下你只是想做一次简单的查询,却要引入整个 ORM 的复杂生命周期,而且 ORM 生成的 SQL 往往不如手写的精确。
此外,还有一个隐藏的痛点:**数据库连接池和游标生命周期管理**。`psycopg2` 的裸用法要求你手动处理连接、提交、回滚、关闭,稍有不慎就会造成连接泄漏。
selfdb 试图在这两个极端之间找到一条中间路线:**保留 SQL 的灵活性和性能,但去掉所有样板代码,并让结果集自动变成 Python 对象**。
## selfdb 是什么?
selfdb(Self-Database)是一个基于 `psycopg2` 的薄封装层,核心思想非常简单:
- 你写 SQL,但不需要管游标和连接细节。
- 查询结果自动转换为 Python 对象,属性名对应列名。
- 支持链式调用、自动提交、上下文管理器,代码量减少 50% 以上。
它的灵感来自 Rust 的 `sqlx` 和 Go 的 `sqlc`——都是“轻量但类型安全”的数据库访问方式,但 selfdb 更贴近 Python 的动态特性。
## 快速上手
### 安装
bash
pip install selfdb
需要 Python 3.8+,且本机有 PostgreSQL 服务。
### 基础用法
假设你有一个数据库 `testdb`,其中有表 `users`(id, name, email)。
python
import selfdb
# 1. 建立连接(全局配置一次即可)
selfdb.configure(
host='localhost',
port=5432,
database='testdb',
user='postgres',
password='secret'
)
# 2. 查询——返回对象列表
db = selfdb.Database()
users = db.query("SELECT id, name, email FROM users WHERE id > %s", 10)
for u in users:
print(u.id, u.name, u.email) # 直接访问属性,不再是 dict
# 3. 插入、更新、删除——自动提交(可关闭)
db.execute("INSERT INTO users (name, email) VALUES (%s, %s)", 'Alice', 'alice@example.com')
# 默认自动 commit,无需手动调用
# 4. 事务控制
with db.transaction():
db.execute("UPDATE users SET name = %s WHERE id = %s", 'Bob', 1)
db.execute("DELETE FROM users WHERE id = %s", 2)
# 事务块内自动 commit/rollback
### 核心亮点:对象映射与动态属性
selfdb 最独特的特性是**结果集自动映射为动态对象**。它不依赖任何 schema 定义或模型类,而是利用 Python 的 `__dict__` 和 `namedtuple` 机制,按列名生成属性。这意味着:
- 你不需要预先定义 `User` 类。
- 列名就是属性名,IDE 可以自动补全(因为对象类型是动态的,但你可以通过类型注解辅助)。
- 嵌套查询、JOIN 结果也能自然映射(冲突列名会加上表名前缀)。
例如:
python
rows = db.query("SELECT u.id, u.name, o.total FROM users u JOIN orders o ON o.user_id = u.id")
for r in rows:
print(r.id, r.name, r.total) # 无需关心来源表
### 高级特性:查询构建器(可选)
虽然 selfdb 主打裸 SQL,但它也提供了一个轻量的查询构建器,用于动态拼接条件:
python
from selfdb import Query
q = Query("users")
q.filter(id__gt=10).filter(name__like="%A%").order_by("-id")
rows = q.all() # 生成 SQL 并执行
这个构建器非常克制,只处理最常见的 WHERE、ORDER BY、LIMIT,不涉及 JOIN 和子查询——那些场景你直接写 SQL 更清晰。
## 核心亮点总结
1. **极简 API**:只有 `query`、`execute`、`transaction` 三个核心方法,学习成本几乎为零。
2. **自动对象映射**:告别 `row[0]` 或 `row['name']`,直接 `row.name`,代码更可读。
3. **自动提交与事务上下文**:默认自动提交,事务用 `with` 块管理,杜绝连接泄漏。
4. **类型安全(可选)**:通过 Python 3.9+ 的 `TypeVar` 和泛型,你可以让 `query` 返回指定类型,获得静态检查支持。
5. **零依赖**:只依赖 `psycopg2-binary`,无其他安装负担。
6. **性能接近裸 SQL**:没有 ORM 的映射开销和查询生成开销,只比手写游标多一层薄封装。
## 适用场景
- **微服务**:每个服务只需简单的 CRUD,不需要完整 ORM。
- **数据分析脚本**:快速从 PostgreSQL 拉取数据,直接处理成对象。
- **已有复杂 SQL 的项目**:你不想重写业务逻辑为 ORM 风格,只想把结果集变成对象。
- **教学与原型开发**:快速验证想法,不必陷入 ORM 配置。
- **性能敏感但对裸 SQL 感到繁琐的场景**:selfdb 避免了 ORM 的 N+1 查询问题,因为一切 SQL 由你掌控。
## 与其他项目对比
| 特性 | selfdb | SQLAlchemy Core | SQLAlchemy ORM | psycopg2 裸用 |
|------|--------|----------------|----------------|---------------|
| 学习曲线 | 极低 | 中等 | 高 | 低 |
| 结果类型 | 动态对象 | Row/字典 | 模型实例 | 元组/字典 |
| SQL 控制力 | 完全 | 完全(通过 text()) | 较低(需调优) | 完全 |
| 自动提交 | 有 | 需手动 | 需手动 | 无 |
| 事务管理 | with 块 | 需手动 | Session | 需手动 |
| 依赖重量 | 轻(1个包) | 中 | 重 | 轻 |
| 类型提示 | 可选泛型 | Row 类型 | 模型类 | 无 |
| 适用规模 | 中小型项目 | 中大型 | 大型复杂域 | 任何 |
**与 SQLAlchemy Core 对比**:SQLAlchemy Core 提供了类似 `select()` 的表达式语言,但它的学习曲线更陡,且返回的 `Row` 对象是只读的。selfdb 允许你对返回对象直接赋值属性(虽然不建议),更贴近 Python 的动态风格。
**与 Django ORM 对比**:Django ORM 是重量级方案,自带迁移、管理后台、关系加载。selfdb 完全没有这些,它只做一件事:把 SQL 结果变成对象。如果你不需要 Django 全家桶,selfdb 更轻。
**与 asyncpg 对比**:asyncpg 是异步驱动,性能极高,但需要手动处理类型转换和游标。selfdb 是同步的,基于 psycopg2,适合传统 Web 框架(Flask、FastAPI 的同步路由)和脚本。
## 潜在不足
- **仅支持 PostgreSQL**:不适用于 MySQL、SQLite。
- **同步设计**:不支持 asyncio(如果你用 asyncpg 生态,请另寻他法)。
- **无迁移工具**:你需要自己管理表结构变更。
- **动态对象缺乏严格的类型安全**:虽然可以用泛型,但运行时仍可能因列名拼写错误而崩溃。
- **社区较小**:目前只有 474 个 star,bug 修复和文档完善可能不如大项目及时。
## 结论
selfdb 是一个“小而美”的工具,它精准地解决了 Python 开发者在数据库访问中的常见痛点:写 SQL 但不想处理游标、想用对象但不想引入 ORM。它适合那些追求简洁、对 SQL 有掌控力、不想被框架绑架的开发者。如果你正在做一个中小型项目,并且使用 PostgreSQL,selfdb 值得一试。
> 项目链接:https://github.com/fzakaria/selfdb