基础用法¶
第一次使用¶
1. 创建引擎¶
from sqlalchemy import create_engine
# 同步引擎
engine = create_engine('couchdb://admin:password@localhost:5984/mydb')
# 异步引擎
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine('couchdb+async://admin:password@localhost:5984/mydb')
2. 定义表结构¶
from sqlalchemy import Table, Column, Integer, String, MetaData
metadata = MetaData()
# 注意:在 CouchDB 中,"表"实际上是通过 `type` 字段区分的
users = Table('users', metadata,
Column('_id', String, primary_key=True), # CouchDB 主键
Column('_rev', String), # 版本号(用于乐观锁)
Column('name', String),
Column('age', Integer),
Column('email', String),
Column('type', String) # 用于区分文档类型
)
# 创建表(在 CouchDB 中会创建视图)
metadata.create_all(engine)
3. 插入数据¶
from sqlalchemy import text
# 方式1: 使用原生 SQL
with engine.connect() as conn:
conn.execute(text("""
INSERT INTO users (_id, name, age, email, type)
VALUES (:id, :name, :age, :email, 'user')
"""), {
'id': 'user:123',
'name': 'Alice',
'age': 30,
'email': 'alice@example.com'
})
conn.commit()
# 方式2: 使用字典(更常用)
with engine.connect() as conn:
result = conn.execute(text("""
INSERT INTO users (_id, name, age, email, type)
VALUES (:_id, :name, :age, :email, 'user')
RETURNING *
"""), {
'_id': 'user:124',
'name': 'Bob',
'age': 25,
'email': 'bob@example.com'
})
row = result.fetchone()
print(f"插入的文档ID: {row._id}")
conn.commit()
4. 查询数据¶
from sqlalchemy import text
with engine.connect() as conn:
# 简单查询
result = conn.execute(text("""
SELECT * FROM users WHERE type = 'user'
"""))
for row in result:
print(f"ID: {row._id}, 姓名: {row.name}, 年龄: {row.age}")
# 带条件查询
result = conn.execute(text("""
SELECT * FROM users
WHERE type = 'user' AND age > :age
ORDER BY age DESC
LIMIT 10
"""), {'age': 20})
for row in result:
print(f"{row.name}: {row.age} 岁")
conn.commit()
5. 更新数据¶
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("""
UPDATE users
SET age = :age
WHERE _id = :id AND type = 'user'
RETURNING *
"""), {
'id': 'user:123',
'age': 31
})
updated_row = result.fetchone()
print(f"更新后的文档: {updated_row._id}, _rev: {updated_row._rev}")
conn.commit()
6. 删除数据¶
from sqlalchemy import text
with engine.connect() as conn:
conn.execute(text("""
DELETE FROM users
WHERE _id = :id AND type = 'user'
"""), {
'id': 'user:123'
})
conn.commit()
文档结构¶
CouchDB 文档格式¶
在 CouchDB 中,每个文档都是 JSON 对象:
{
"_id": "user:123",
"_rev": "1-abc123def456",
"type": "users",
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
字段说明:
- _id: 文档唯一标识符
- _rev: 版本号(用于乐观锁)
- type: 文档类型(用于模拟"表")
- 其他字段: 自定义数据
类型字段约定¶
| 表名 | type 字段值 | 示例 _id |
|---|---|---|
users |
users |
user:123 |
orders |
orders |
order:456 |
products |
products |
product:789 |
最佳实践¶
1. 使用上下文管理器¶
# ✅ 推荐
with engine.connect() as conn:
conn.execute(text("..."))
conn.commit()
# ❌ 不推荐
conn = engine.connect()
conn.execute(text("..."))
conn.commit()
conn.close()
2. 参数化查询¶
# ✅ 推荐:安全,防止 SQL 注入
conn.execute(text("SELECT * FROM users WHERE age > :age"), {'age': 20})
# ❌ 危险:SQL 注入风险
conn.execute(text(f"SELECT * FROM users WHERE age > {age}"))
3. 使用事务¶
with engine.begin() as conn: # 自动 commit
conn.execute(text("INSERT INTO users ..."))
conn.execute(text("INSERT INTO orders ..."))
# 自动 commit
# 或手动控制
with engine.connect() as conn:
try:
conn.execute(text("INSERT INTO users ..."))
conn.execute(text("INSERT INTO orders ..."))
conn.commit()
except Exception as e:
conn.rollback()
raise
4. 批量操作¶
# 批量插入
with engine.connect() as conn:
users_data = [
{'_id': 'user:1', 'name': 'Alice', 'type': 'users'},
{'_id': 'user:2', 'name': 'Bob', 'type': 'users'},
{'_id': 'user:3', 'name': 'Charlie', 'type': 'users'},
]
for user_data in users_data:
conn.execute(text("""
INSERT INTO users (_id, name, type)
VALUES (:_id, :name, :type)
"""), user_data)
conn.commit()
常见错误¶
1. 乐观锁错误¶
# 如果文档已被修改,会抛出异常
# 解决方案:重新获取最新文档
try:
conn.execute(text("UPDATE users SET age=31 WHERE _id='user:123'"))
conn.commit()
except Exception as e:
# 重新获取文档
result = conn.execute(text("SELECT * FROM users WHERE _id='user:123'"))
latest_doc = result.fetchone()
# 重新尝试更新
2. 文档不存在¶
# 检查文档是否存在
result = conn.execute(text("SELECT * FROM users WHERE _id = :id"), {'id': 'user:999'})
if result.rowcount == 0:
print("文档不存在")
else:
row = result.fetchone()