Skip to content

Commit 775d578

Browse files
committed
Migrating from mkdocs to zensical
1 parent 396f900 commit 775d578

19 files changed

Lines changed: 1029 additions & 1892 deletions

.github/workflows/docs.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@ jobs:
2222
- name: Install uv
2323
uses: astral-sh/setup-uv@v6
2424

25-
- name: Set up Python ${{ matrix.python-version }}
26-
run: uv python install ${{ matrix.python-version }}
25+
- name: Set up Python
26+
run: uv python install 3.10
2727

2828
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
2929

3030
- uses: actions/cache@v4
3131
with:
32-
key: mkdocs-material-${{ env.cache_id }}
32+
key: zensical-${{ env.cache_id }}
3333
path: .cache
3434
restore-keys: |
35-
mkdocs-material-
35+
zensical-
3636
37-
- run: sudo apt-get install pngquant
3837
- run: uv sync --group docs
39-
- run: uv run mkdocs gh-deploy --force
38+
- run: uv run zensical build -f zensical.toml --clean
39+
- run: uv run ghp-import -n -p -f site
4040

4141
env:
4242
GH_TOKEN: ${{ secrets.GH_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ __pycache__/
55
venv/
66
.idea/
77
dist/
8+
site/
89
.pytest_cache/
910
.ruff_cache/
1011
.claude/

docs/advanced/filter.md

Lines changed: 46 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,39 @@
11
# 过滤条件
22

3-
SQLAlchemy CRUD Plus 支持 30+ 种过滤操作符,用于构建复杂的查询条件。
4-
5-
## 基础用法
6-
7-
```python
8-
# 使用过滤条件查询
9-
users = await user_crud.select_models(
10-
session,
11-
name="张三", # 等于
12-
age__gt=18, # 大于
13-
email__like="%@qq.com" # 模糊匹配
14-
)
15-
```
16-
17-
## 比较操作符
3+
过滤条件通过关键字参数传入:`字段名__操作符=值`。没有操作符时表示等于,例如 `name='张三'`
184

195
```python
20-
# 数值比较
216
users = await user_crud.select_models(
227
session,
23-
age__gt=30, # 大于 30
24-
age__ge=18, # 大于等于 18
25-
age__lt=65, # 小于 65
26-
age__le=60, # 小于等于 60
27-
id__eq=1, # 等于 1
28-
status__ne=0 # 不等于 0
8+
name__like='%张%',
9+
age__ge=18,
10+
is_active=True
2911
)
3012
```
3113

32-
## 范围操作符
33-
34-
```python
35-
# 包含查询
36-
users = await user_crud.select_models(
37-
session,
38-
id__in=[1, 2, 3, 4, 5], # ID 在列表中
39-
status__not_in=[0, -1], # 状态不在列表中
40-
age__between=[18, 65] # 年龄在 18-65 之间
41-
)
42-
```
14+
## 常用操作符
4315

44-
## 字符串操作符
16+
| 类型 | 操作符 | 示例 | 说明 |
17+
| --- | --- | --- | --- |
18+
| 等值 | 无 / `__eq` | `name='张三'``id__eq=1` | 等于 |
19+
| 比较 | `__gt` / `__ge` | `age__gt=18` | 大于 / 大于等于 |
20+
| 比较 | `__lt` / `__le` | `age__le=60` | 小于 / 小于等于 |
21+
| 不等 | `__ne` | `status__ne=0` | 不等于 |
22+
| 集合 | `__in` / `__not_in` | `id__in=[1, 2]` | 在 / 不在列表中 |
23+
| 范围 | `__between` | `age__between=[18, 65]` | 闭区间范围 |
24+
| 字符串 | `__like` / `__not_like` | `name__like='%张%'` | LIKE / NOT LIKE |
25+
| 字符串 | `__ilike` / `__not_ilike` | `name__ilike='%admin%'` | 忽略大小写匹配 |
26+
| 字符串 | `__startswith` | `email__startswith='admin'` | 前缀匹配 |
27+
| 字符串 | `__endswith` | `email__endswith='@example.com'` | 后缀匹配 |
28+
| 字符串 | `__contains` | `bio__contains='Python'` | 包含 |
29+
| 空值 | `__is` / `__is_not` | `deleted_at__is=None` | IS / IS NOT |
4530

46-
```python
47-
# 字符串匹配
48-
users = await user_crud.select_models(
49-
session,
50-
name__like='%张%', # 包含"张"
51-
name__not_like='%test%', # 不包含"test"
52-
name__ilike='%ADMIN%', # 不区分大小写包含
53-
email__startswith='admin', # 以"admin"开头
54-
email__endswith='@qq.com', # 以"@qq.com"结尾
55-
bio__contains='程序员', # 包含"程序员"
56-
)
57-
```
31+
## OR 条件
5832

59-
## 空值检查
33+
使用特殊键 `__or__` 表示 OR。外层其他条件仍然是 AND。
6034

6135
```python
62-
# NULL 值处理
63-
users = await user_crud.select_models(
64-
session,
65-
deleted_at__is=None, # 为 NULL
66-
profile_id__is_not=None, # 不为 NULL
67-
)
68-
```
69-
70-
## OR 条件查询
71-
72-
### 同字段多值
73-
74-
```python
75-
# 邮箱域名为 gmail.com 或 qq.com 的用户
76-
users = await user_crud.select_models(
77-
session,
78-
__or__={
79-
'email__endswith': ['@gmail.com', '@qq.com']
80-
}
81-
)
82-
```
83-
84-
### 不同字段条件
85-
86-
```python
87-
# 名字包含"张"或邮箱以admin开头的用户
36+
# 名称包含“张”,或邮箱以 admin 开头。
8837
users = await user_crud.select_models(
8938
session,
9039
__or__={
@@ -94,192 +43,76 @@ users = await user_crud.select_models(
9443
)
9544
```
9645

97-
### 复杂 OR 条件
46+
同一个字段需要多个 OR 值时,传列表:
9847

9948
```python
100-
# 多种条件组合
10149
users = await user_crud.select_models(
10250
session,
103-
is_active=True, # 必须是活跃用户
51+
is_active=True,
10452
__or__={
105-
'level__ge': 5, # 等级大于等于5
106-
'is_vip': True, # 或者是VIP
107-
'total_spent__gt': 1000 # 或者消费大于1000
53+
'email__endswith': ['@gmail.com', '@qq.com']
10854
}
10955
)
11056
```
11157

112-
## 操作符参考
113-
114-
### 比较操作符
115-
116-
| 操作符 | 说明 | 示例 |
117-
|--------|------|----------------|
118-
| `__gt` | 大于 | `age__gt=18` |
119-
| `__ge` | 大于等于 | `age__ge=18` |
120-
| `__lt` | 小于 | `age__lt=65` |
121-
| `__le` | 小于等于 | `age__le=65` |
122-
| `__eq` | 等于 | `id__eq=1` |
123-
| `__ne` | 不等于 | `status__ne=0` |
124-
125-
### 包含操作符
126-
127-
| 操作符 | 说明 | 示例 |
128-
|-------------|-------|------------------------|
129-
| `__in` | 在列表中 | `id__in=[1,2,3]` |
130-
| `__not_in` | 不在列表中 | `id__not_in=[1,2,3]` |
131-
| `__between` | 在范围内 | `age__between=[18,65]` |
132-
133-
### 字符串操作符
134-
135-
| 操作符 | 说明 | 示例 |
136-
|----------------|-------------|-----------------------------|
137-
| `__like` | 模糊匹配 | `name__like='%张%'` |
138-
| `__not_like` | 模糊不匹配 | `name__not_like='%test%'` |
139-
| `__ilike` | 不区分大小写模糊匹配 | `name__ilike='%ZHANG%'` |
140-
| `__not_ilike` | 不区分大小写模糊不匹配 | `name__not_ilike='%TEST%'` |
141-
| `__startswith` | 开头匹配 | `email__startswith='admin'` |
142-
| `__endswith` | 结尾匹配 | `email__endswith='@qq.com'` |
143-
| `__contains` | 包含 | `name__contains='张'` |
144-
145-
### 空值操作符
146-
147-
| 操作符 | 说明 | 示例 |
148-
|------------|-------|---------------------------|
149-
| `__is` | 为空检查 | `deleted_at__is=None` |
150-
| `__is_not` | 不为空检查 | `deleted_at__is_not=None` |
151-
152-
## 实际应用示例
153-
154-
### 用户搜索功能
58+
## 组合示例
15559

15660
```python
157-
async def search_users(
158-
session: AsyncSession,
159-
keyword: str = None,
160-
age_min: int = None,
161-
age_max: int = None,
162-
is_active: bool = None
163-
):
164-
filters = {}
61+
async def search_users(session: AsyncSession, keyword: str | None = None):
62+
filters = {'is_active': True}
16563

166-
# 关键词搜索(姓名或邮箱)
16764
if keyword:
16865
filters['__or__'] = {
16966
'name__like': f'%{keyword}%',
17067
'email__like': f'%{keyword}%'
17168
}
17269

173-
# 年龄范围
174-
if age_min is not None:
175-
filters['age__ge'] = age_min
176-
if age_max is not None:
177-
filters['age__le'] = age_max
178-
179-
# 状态筛选
180-
if is_active is not None:
181-
filters['is_active'] = is_active
182-
183-
return await user_crud.select_models(session, **filters)
70+
return await user_crud.select_models(
71+
session,
72+
**filters,
73+
limit=20
74+
)
18475
```
18576

186-
### 高级筛选
187-
18877
```python
189-
# 查找活跃的高级用户
190-
users = await user_crud.select_models(
78+
users = await user_crud.select_models_order(
19179
session,
192-
is_active=True,
80+
sort_columns='created_at',
81+
sort_orders='desc',
19382
created_at__ge='2024-01-01',
194-
__or__={
195-
'level__ge': 5,
196-
'is_vip': True,
197-
'total_orders__gt': 10
198-
}
83+
age__between=[18, 65],
84+
status__not_in=['blocked', 'deleted']
19985
)
20086
```
20187

202-
## 复合主键支持
203-
204-
SQLAlchemy CRUD Plus 自动检测模型主键,支持单个主键和复合主键。
88+
## 复合主键
20589

206-
### 复合主键模型
90+
CRUDPlus 会自动识别单主键和复合主键。复合主键查询、更新、删除时用元组传入 `pk`
20791

20892
```python
20993
class UserRole(Base):
21094
__tablename__ = 'user_roles'
21195

212-
# 复合主键
21396
user_id: Mapped[int] = mapped_column(primary_key=True)
21497
role_id: Mapped[int] = mapped_column(primary_key=True)
215-
assigned_at: Mapped[datetime] = mapped_column(DateTime)
21698
```
21799

218-
### 复合主键操作
219-
220100
```python
221-
# 创建
222-
user_role_data = UserRoleCreate(user_id=1, role_id=2)
223-
user_role = await user_role_crud.create_model(session, user_role_data)
224-
225-
# 查询(使用元组)
226101
user_role = await user_role_crud.select_model(session, pk=(1, 2))
227102

228-
# 更新
229103
await user_role_crud.update_model(
230104
session,
231105
pk=(1, 2),
232-
obj={"assigned_at": datetime.now()}
106+
obj={'role_name': 'admin'}
233107
)
234108

235-
# 删除
236109
await user_role_crud.delete_model(session, pk=(1, 2))
237-
238-
# 批量查询
239-
user_roles = await user_role_crud.select_models(session, user_id=1)
240110
```
241111

242112
## 性能建议
243113

244-
### 索引优化
245-
246-
```python
247-
# 为常用查询字段创建索引
248-
class User(Base):
249-
__tablename__ = 'users'
250-
251-
email: Mapped[str] = mapped_column(String(100), unique=True, index=True)
252-
is_active: Mapped[bool] = mapped_column(default=True, index=True)
253-
created_at: Mapped[datetime] = mapped_column(DateTime, index=True)
254-
255-
# 复合索引
256-
__table_args__ = (
257-
Index('idx_user_active_created', 'is_active', 'created_at'),
258-
)
259-
```
260-
261-
### 查询优化技巧
262-
263-
```python
264-
# 使用 exists 检查存在性(更高效)
265-
exists = await user_crud.exists(session, email="test@example.com")
266-
267-
# 使用 limit 限制结果集
268-
recent_users = await user_crud.select_models(
269-
session,
270-
created_at__ge=datetime.now() - timedelta(days=7),
271-
limit=100
272-
)
273-
274-
# 避免前缀通配符(低效)
275-
# 不推荐: name__like='%张%'
276-
# 推荐: name__like='张%'
277-
```
278-
279-
## 注意事项
280-
281-
1. **参数命名**: 主键参数使用 `pk`,避免与常见字段名 `id` 混淆
282-
2. **自动主键检测**: 支持各种主键类型,包括字符串主键和复合主键
283-
3. **性能考虑**: 为常用过滤字段创建数据库索引
284-
4. **OR 查询**: 过多 OR 条件可能影响性能,合理使用
285-
5. **通配符**: 避免以通配符开头的 LIKE 查询
114+
- 常用过滤字段应建立索引,例如 `email``status``created_at`
115+
- `exists()` 比查询整条记录更适合做存在性检查。
116+
- 列表查询务必配合 `limit`,避免一次加载过多数据。
117+
- `LIKE '%keyword%'` 通常无法有效利用普通索引;能用前缀匹配时优先 `LIKE 'keyword%'`
118+
- OR 条件过多时,建议评估 SQL 执行计划。

0 commit comments

Comments
 (0)