Skip to content

Commit d56a703

Browse files
authored
Add count and exists usages (#37)
* Add count and exists usages * fix lint
1 parent d01b2d4 commit d56a703

28 files changed

+676
-345
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v5.0.0
44
hooks:
55
- id: check-added-large-files
66
- id: end-of-file-fixer
77
- id: check-toml
88

99
- repo: https://github.com/charliermarsh/ruff-pre-commit
10-
rev: v0.7.2
10+
rev: v0.11.5
1111
hooks:
1212
- id: ruff
1313
args:
@@ -16,7 +16,7 @@ repos:
1616
- id: ruff-format
1717

1818
- repo: https://github.com/astral-sh/uv-pre-commit
19-
rev: 0.4.29
19+
rev: 0.6.14
2020
hooks:
2121
- id: uv-lock
2222
- id: uv-export

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# sqlalchemy-crud-plus
22

3-
Asynchronous CRUD operations based on SQLAlChemy 2.0
3+
基于 SQLAlChemy 2.0 的异步 CRUD 操作
44

55
## Download
66

@@ -14,7 +14,7 @@ pip install sqlalchemy-crud-plus
1414

1515
## 互动
1616

17-
[TG / Discord](https://wu-clan.github.io/homepage/)
17+
[Discord](https://wu-clan.github.io/homepage/)
1818

1919
## 赞助
2020

docs/advanced/commit.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
SQLAlchemy CRUD Plus 内部很多方法都提供了 `commit` 参数,默认值为 `False`,它既不会执行提交操作,也不包含 `flush`
1+
SQLAlchemy CRUD Plus 内部为很多方法都提供了 `commit` 参数,默认值为 `False`,它既不会执行提交操作,也不包含 `flush`
22
等行为,要想真正写入到数据库,你可以通过以下几种方案
33

44
## `commit=True`
@@ -9,6 +9,8 @@ SQLAlchemy CRUD Plus 将在内部自动执行提交
99
```py hl_lines="31"
1010
from fastapi import FastAPI, Depends
1111

12+
from pydantic import BaseModel
13+
1214
--8<-- "docs/ext/get_db.py"
1315

1416
app = FastAPI()
@@ -32,7 +34,6 @@ async def create(self, obj: CreateIns, db: AsyncSession = Depends(get_db)) -> No
3234
```py hl_lines="9"
3335
--8<-- "docs/ext/async_db_session.py"
3436

35-
3637
async def create(self, obj: CreateIns) -> None:
3738
async with async_db_session.begin() as db:
3839
await self.create_model(db, obj)

docs/advanced/filter.md

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
11
SQLAlchemy CRUD Plus 支持高级过滤选项,允许使用运算符查询记录,如大于(`__gt`)、小于(`__lt`);
22

3-
大多数过滤器操作符需要一个字符串或整数值
4-
5-
```python
6-
# 获取年龄大于 30 岁以上的员工
7-
items = await item_crud.select_models(
8-
session=db,
9-
age__gt=30,
10-
)
11-
```
12-
133
## 比较运算符
144

155
- `__gt`:大于
@@ -20,6 +10,14 @@ items = await item_crud.select_models(
2010
- `__ne`: 不等于
2111
- `__between`: 在两者之间
2212

13+
```python title="e.g."
14+
# 获取年龄大于 30 岁以上的员工
15+
items = await item_crud.select_models(
16+
session=db,
17+
age__gt=30,
18+
)
19+
```
20+
2321
## IN 比较
2422

2523
- `__in`: 包含
@@ -30,7 +28,7 @@ items = await item_crud.select_models(
3028
- `__is`:用于测试 “真”、“假” 和 “无”。
3129
- `__is_not`:“is” 的否定
3230
- `__is_distinct_from`: 产生 SQL IS DISTINCT FROM
33-
- `__is_not_distinct_from`: Produces SQL IS NOT DISTINCT FROM
31+
- `__is_not_distinct_from`: 产生 SQL IS NOT DISTINCT FROM
3432
- `__like`:针对特定文本模式的 SQL “like” 搜索
3533
- `__not_like`:“like” 的否定
3634
- `__ilike`:大小写不敏感的 “like”
@@ -52,7 +50,13 @@ items = await item_crud.select_models(
5250

5351
## 算术运算符
5452

55-
此过滤器使用方法需查看:[算数](#_7)
53+
!!! note
54+
55+
此过滤器必须传递字典,且字典结构必须为 `{'value': xxx, 'condition': {'已支持的过滤器': xxx}}`
56+
57+
`value`:此值将与列值进行条件运算
58+
59+
`condition`:此值将作为运算条件和预期结果
5660

5761
- `__add`: Python `+` 运算符
5862
- `__radd`: Python `+` 反向运算
@@ -67,6 +71,14 @@ items = await item_crud.select_models(
6771
- `__mod`: Python `%` 运算符
6872
- `__rmod`: Python `%` 反向运算
6973

74+
```python title="e.g."
75+
# 获取薪资打八折以后仍高于 20k 的员工
76+
items = await item_crud.select_models(
77+
session=db,
78+
payroll__mul={'value': 0.8, 'condition': {'gt': 20000}},
79+
)
80+
```
81+
7082
## BETWEEN、IN、NOT IN
7183

7284
!!! note
@@ -84,7 +96,7 @@ items = await item_crud.select_models(
8496

8597
## AND
8698

87-
可以通过将多个过滤器链接在一起来实现 AND 子句
99+
当多个过滤器同时存在时,将自动转为 AND 子句
88100

89101
```python
90102
# 获取年龄在 30 以上,薪资大于 20k 的员工
@@ -142,21 +154,3 @@ items = await item_crud.select_models(
142154
]
143155
)
144156
```
145-
146-
## 算数
147-
148-
!!! note
149-
150-
此过滤器必须传递字典,且字典结构必须为 `{'value': xxx, 'condition': {'已支持的过滤器': xxx}}`
151-
152-
`value`:此值将与列值进行运算
153-
154-
`condition`:此值将作为运算后的比较值,比较条件取决于使用的过滤器
155-
156-
```python
157-
# 获取薪资打八折以后仍高于 20k 的员工
158-
items = await item_crud.select_models(
159-
session=db,
160-
payroll__mul={'value': 0.8, 'condition': {'gt': 20000}},
161-
)
162-
```

docs/advanced/primary_key.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
!!! note 主键参数命名
22

3-
由于在 python 内部 id 的特殊性,我们设定 pk (参考 Django) 作为模型主键命名,所以在 crud 方法中,任何涉及到主键的地方,入参都为 `pk`
3+
由于在 python 内部 `id` 为关键字,因此,我们设定默认主键入参为 `pk`。这仅用于函数入参,并不要求模型主键必须定义为 `pk`
44

55
```py title="e.g." hl_lines="2"
66
async def delete(self, db: AsyncSession, primary_key: int) -> int:
@@ -12,11 +12,10 @@ async def delete(self, db: AsyncSession, primary_key: int) -> int:
1212
!!! tip 自动主键
1313

1414
我们在 SQLAlchemy CRUD Plus 内部通过 [inspect()](https://docs.sqlalchemy.org/en/20/core/inspection.html) 自动搜索表主键,
15-
而非强制绑定主键列必须命名为 id,感谢 [@DavidSche](https://github.com/DavidSche) 提供帮助
15+
而非强制绑定主键列必须命名为 `id`
1616

1717
```py title="e.g." hl_lines="4"
1818
class ModelIns(Base):
19-
# your sqlalchemy model
20-
# define your primary_key
21-
custom_id: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
19+
# define primary_key
20+
primary_key: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
2221
```

docs/ext/get_db.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
1+
from typing import AsyncGenerator
2+
3+
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
24

35
async_engine = create_async_engine('数据库连接', future=True)
46
async_db_session = async_sessionmaker(async_engine, autoflush=False, expire_on_commit=False)
57

68

7-
async def get_db() -> AsyncSession:
9+
async def get_db() -> AsyncGenerator:
810
"""
911
session 生成器
1012
"""
11-
session = async_db_session()
12-
try:
13+
async with async_db_session() as session:
1314
yield session
14-
except Exception as se:
15-
await session.rollback()
16-
raise se
17-
finally:
18-
await session.close()

docs/ext/model.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

docs/usage/count.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
查询符合条件过滤的记录数
2+
3+
```py title="count" hl_lines="14"
4+
from sqlalchemy_crud_plus import CRUDPlus
5+
6+
from sqlalchemy import DeclarativeBase as Base
7+
from sqlalchemy.ext.asyncio import AsyncSession
8+
9+
10+
class ModelIns(Base):
11+
# your sqlalchemy model
12+
pass
13+
14+
15+
class CRUDIns(CRUDPlus[ModelIns]):
16+
async def create(self, db: AsyncSession) -> int:
17+
return await self.count(db, name="foo")
18+
```
19+
20+
## API
21+
22+
```python
23+
async def count(
24+
self,
25+
session: AsyncSession,
26+
filters: ColumnElement | list[ColumnElement] | None = None,
27+
**kwargs,
28+
) -> int:
29+
```
30+
31+
**Parameters:**
32+
33+
| Name | Type | Description | Default |
34+
|---------|----------------------------------------------------|------------------|---------|
35+
| session | AsyncSession | 数据库会话 | 必填 |
36+
| filters | `ColumnElement `\|` list[ColumnElement] `\|` None` | 要应用于查询的 WHERE 子句 | `None` |
37+
38+
!!! note "**kwargs"
39+
40+
[条件过滤](../advanced/filter.md),将创建条件查询 SQL
41+
42+
**Returns:**
43+
44+
| Type | Description |
45+
|------|-------------|
46+
| int | 记录数 |

docs/usage/create_model.md

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
1-
````py
1+
添加单条新纪录
2+
3+
```py title="create_model" hl_lines="21"
4+
from pydantic import BaseModel
5+
6+
from sqlalchemy_crud_plus import CRUDPlus
7+
8+
from sqlalchemy import DeclarativeBase as Base
9+
from sqlalchemy.ext.asyncio import AsyncSession
10+
11+
12+
class ModelIns(Base):
13+
# your sqlalchemy model
14+
pass
15+
16+
17+
class CreateIns(BaseModel):
18+
# your pydantic schema
19+
pass
20+
21+
22+
class CRUDIns(CRUDPlus[ModelIns]):
23+
async def create(self, db: AsyncSession, obj: CreateIns) -> ModelIns:
24+
return await self.create_model(db, obj)
25+
```
26+
27+
## API
28+
29+
```py
230
async def create_model(
331
self,
432
session: AsyncSession,
@@ -7,12 +35,18 @@ async def create_model(
735
commit: bool = False,
836
**kwargs,
937
) -> Model:
10-
````
38+
```
1139

12-
- 此方法提供 `flush` 参数,详见:[冲洗](../advanced/flush.md)
13-
- 此方法提供 `commit` 参数,详见:[提交](../advanced/commit.md)
40+
**Parameters:**
1441

15-
!!! note "关键字参数"
42+
| Name | Type | Description | Default |
43+
|---------|--------------|-----------------------------|---------|
44+
| session | AsyncSession | 数据库会话 | 必填 |
45+
| obj | TypeVar | 创建新数据参数 | 必填 |
46+
| flush | bool | [冲洗](../advanced/flush.md) | `False` |
47+
| commit | bool | [提交](../advanced/commit.md) | `False` |
48+
49+
!!! note "**kwargs"
1650

1751
除了必须传入 obj schema 外,还可以通过关键字入参,传入非 schema
1852
参数,比如,对于某些特定场景,其中一个字段并不是通用的,也不需要进行输入控制,只需在写入时赋予指定值,那么你可以使用关键字入参即可
@@ -28,30 +62,8 @@ async def create_model(
2862
1. 在数据被最终写入前,所有入参(schema 和关键字参数)都会赋值给 SQLAlchemy 模型,即便你传入了非模型数据,
2963
这也是安全的,因为它不会被模型所引用
3064

65+
**Returns:**
3166

32-
33-
## 示例
34-
35-
```py title="create_model" hl_lines="21"
36-
from pydantic import BaseModel
37-
38-
from sqlalchemy_crud_plus import CRUDPlus
39-
40-
from sqlalchemy import DeclarativeBase as Base
41-
from sqlalchemy.ext.asyncio import AsyncSession
42-
43-
44-
class ModelIns(Base):
45-
# your sqlalchemy model
46-
pass
47-
48-
49-
class CreateIns(BaseModel):
50-
# your pydantic schema
51-
pass
52-
53-
54-
class CRUDIns(CRUDPlus[ModelIns]):
55-
async def create(self, db: AsyncSession, obj: CreateIns) -> ModelIns:
56-
return await self.create_model(db, obj)
57-
```
67+
| Type | Description |
68+
|---------|-------------|
69+
| TypeVar | 模型实例 |

0 commit comments

Comments
 (0)