Files
2026-09-12 23:12:15 +08:00

171 lines
5.2 KiB
Markdown

# Repository层规范
## 概述
Repository 层是数据访问层,封装所有数据库操作。各模块 Repository 继承 `BaseRepository[Model]`,获得通用 CRUD 能力,子类按需添加自定义查询。
:::tip 设计原则
- Repository 只做数据访问原语,不处理 R 封装/序列化/业务逻辑
- 软删除由基类自动处理,子类无需关心
- 查询方法自动跳过 None 值条件
:::
## BaseRepository 继承
以岗位模块为例,`src/modules/system/position/repository.py` 完整内容:
```python
"""modules/system/position 数据访问层。"""
from modules.system.position.models import Position
from core.base_repository import BaseRepository
class PositionRepository(BaseRepository[Position]):
"""岗位数据仓库,继承基础仓库获得通用 CRUD 能力"""
pass
# 模块级单例
position_repo = PositionRepository(Position)
```
:::tip 单例模式
每个模块在文件末尾创建 Repository 实例(如 `position_repo`),供 Service 层使用。避免每次请求创建新实例。基类构造函数接收模型类:`PositionRepository(Position)`。
:::
## 查询方法选择表
| 方法 | 软删过滤 | 自动跳过 None | 适用场景 |
|------|---------|--------------|---------|
| `get_by_id(id)` | 是 | - | 按 ID 查询单条 |
| `get_by_ids(ids)` | 是 | - | 按 ID 列表批量查询 |
| `get_one(**filters)` | 是 | 是 | 按等值条件查单条 |
| `get_all(**filters)` | 是 | 是 | 按等值条件查多条 |
| `count(**filters)` | 是 | 是 | 统计记录数 |
| `exists(obj_id)` | 是 | - | 检查记录是否存在 |
| `exists_by_field(field, value, exclude_id)` | 是 | - | 唯一性校验 |
| `filter_by(**kwargs)` | 是 | 是 | 等值查询,返回 Query |
| `filter(*criterion)` | **否** | - | 原始查询,复杂条件 |
| `paginate(page, size, query)` | 是 | - | 分页查询 |
:::warning filter 与 filter_by 的区别
- `filter_by(**kwargs)`:等值查询,自动软删过滤,跳过 None 值,返回 Query
- `filter(*criterion)`:原始查询,**不自动软删过滤**,用于 `in_`/`!=`/`and_`/区间等复杂条件,需调用方自行追加 `is_delete == 0`
:::
## 常用查询示例
### 等值查询
```python
# 查询单条:自动软删过滤,跳过 None
user = user_repo.get_one(username='admin', status=1)
# 查询多条:自动软删过滤
positions = position_repo.get_all(status=1)
# 统计数量
count = position_repo.count(status=1)
# 唯一性校验
exists = position_repo.exists_by_field('name', '开发工程师', exclude_id=5)
```
### 复杂条件查询
```python
# 使用 filter(需自行追加 is_delete 条件)
users = user_repo.filter(
User.dept_id.in_([1, 2, 3]),
User.is_delete == 0
).all()
# 区间查询
articles = article_repo.filter(
Article.create_time >= start_date,
Article.create_time <= end_date,
Article.is_delete == 0
).all()
# 使用 filter_by + 排序
positions = position_repo.filter_by(status=1).order_by(
Position.sort.asc()
).all()
```
### 分页查询
```python
# 自定义 query 分页
query = position_repo.filter_by(status=1).order_by(Position.sort.asc())
items, total = position_repo.paginate(page_no=1, page_size=10, query=query)
```
## 写操作
### 创建
```python
# 创建记录(不提交事务,由中间件统一管理)
position_repo.create(name='开发工程师', status=1, sort=1)
```
### 更新
```python
# 按 ID 更新,返回受影响行数
count = position_repo.update(obj_id=1, data={'name': '高级开发工程师', 'sort': 2})
```
### 删除
```python
# 软删除,返回实际删除条数
count = position_repo.batch_delete("1,2,3")
```
:::tip 删除的 R 封装
Repository 的 `batch_delete` 只做数据原语,R 响应由 Service 层 `BaseService.batch_delete_with_r(repo, ids)` 统一产出。禁止在 Repository 层直接返回 R 对象。
:::
## 自定义查询方法
子类可添加模块特有的查询方法:
```python
class UserRepository(BaseRepository[User]):
"""用户数据仓库"""
def get_by_username(self, username: str):
"""根据用户名查询"""
return self.get_one(username=username)
def get_users_by_dept(self, dept_id: int):
"""查询部门下所有用户"""
return self.get_all(dept_id=dept_id)
def get_users_with_role(self, role_id: int):
"""查询拥有指定角色的用户"""
return self.filter(
User.is_delete == 0
).join(
UserRole, UserRole.user_id == User.id
).filter(
UserRole.role_id == role_id
).all()
```
## 数据清洗
`create` 和 `update` 方法内置 `_clean_model_data()`,自动将 camelCase 键转为 snake_case,并仅保留模型真实列字段:
```python
# 传入 camelCase 键,自动转换
position_repo.create(positionName='开发', status=1)
# 内部自动转为 position_name → 查找模型列 → 忽略不存在的字段
```
## 总结
Repository 层通过继承 `BaseRepository[Model]` 获得完整的 CRUD 能力。查询方法分两类:等值便捷方法(`get_one`/`get_all`/`filter_by` 等,自动软删过滤)和原始查询出口(`filter`,需自行处理软删)。写操作由 Repository 做数据原语,R 响应由 Service 层统一封装。模块级单例模式避免重复创建实例。