1
- 作为开始, 我们会一步步开始, 返回单层 task 列表的 API 逐渐过渡到返回多层 Teams 列表 API .
1
+ 作为开始, 我们会一步步开始, 从返回单层 task 列表逐渐过渡到返回多层 Teams 列表.
2
2
3
- To begin, we will start step by step, transitioning from an API that returns a single-layer task list to an API that returns a multi-layer Teams list.
3
+ ## 简单列表
4
4
5
- ## 简单列表 Simple list
6
-
7
- 对应的路由:
8
-
9
- routers:
5
+ 路由:
10
6
11
7
- ` sample_1.router:get_users `
12
8
- ` sample_1.router:get_tasks `
13
9
14
10
在` src.router.sample_1 ` 中,我们依次创建 users, tasks 的 API, 以 list[ T] 的形式返回。
15
11
16
- In ` src.router.sample_1 ` , we will sequentially create APIs for users and tasks, returning them in the form of list[ T] .
17
-
18
12
``` python
19
13
import src.services.task.query as tq
20
14
@@ -26,15 +20,20 @@ async def get_step_1_tasks(session: AsyncSession = Depends(db.get_session)):
26
20
27
21
通过引入 ` src.services.user.query ` 和 ` src.services.task.query ` 中的查询,返回了 ` list[orm] ` 对象, 然后 FastAPI 会自动将对象转成 response_model 中对应的类型.
28
22
29
- by importing queries from ` src.services.user.query ` and ` src.services.task.query ` , we can get ` list[orm] ` , and then FastAPI will automatically convert the objects into the corresponding types defined in response_model
23
+ 访问
24
+ - ` http://localhost:8000/sample_1/users `
25
+ - ` http://localhost:8000/sample_1/tasks `
30
26
31
- ## 嵌套列表
32
27
33
- 接下来我们要将将 user 信息添加到 task 中, 在 sample_1 目录下创建 ` schema.py ` , 定义一个扩展了 user 信息的 ` Sample1TaskDetail ` 类型.
28
+ ## 构建嵌套列表
34
29
35
- > 为了避免类型名字重复,使用 router 名字作为前缀
30
+ 接下来我们要将将 user 信息添加到 task 中, 在 sample_1 目录下创建 ` schema.py ` .
31
+
32
+ 然后定义一个扩展了 user 信息的 ` Sample1TaskDetail ` 类型.
33
+
34
+ > 为了避免类型名字重复, 使用 router 名字作为前缀
36
35
>
37
- > 因此 Sample1 开头的 schema 都是属于 sample_1 路由的 (这点在生成前端 sdk ts 类型的时候会很有用 .)
36
+ > 因此 Sample1 开头的 schema 都是属于 sample_1 路由的 (这点在生成前端 sdk ts 类型的时候将会很有用 .)
38
37
39
38
``` python
40
39
class Sample1TaskDetail (ts .Task ):
@@ -49,11 +48,13 @@ class Sample1TaskDetail(ts.Task):
49
48
2 . 定义 user 需要添加默认值, 否则用 ` Sample1TaskDetail.model_valiate ` 会报缺少字段错误.
50
49
3 . ` ul.user_batch_loader ` 会根据 ` list[task.owner_id] ` 来关联 task 和 user 对象. 具体看 ` src.services.user.loader `
51
50
51
+ loader 的作用是提前收集好所有task需要查询的 ` task.owner_id ` , 一次性查询完之后赋值给各自的 task
52
+
52
53
> resolve 返回的数据需要是 pydantic 可以转化的类型.
53
54
>
54
55
> 如果是 orm 对象需要配置 ` ConfigDict(from_attribute=True) `
55
56
56
- 在 ` router.py ` 中, 依然是通过 ` tq.get_tasks(session) ` 来获取初始数据, 接着转换成 ` Sample1TaskDetail ` . 之后交给 ` Resolver ` 就能 resolve 出所有 user 信息.
57
+ 然后在 ` router.py ` 中, 依然是通过 ` tq.get_tasks(session) ` 来获取初始数据, 接着转换成 ` Sample1TaskDetail ` . 之后交给 ` Resolver ` 就能 resolve 出所有 user 信息.
57
58
58
59
``` python
59
60
@route.get (' /tasks-with-detail' , response_model = List[Sample1TaskDetail])
@@ -65,6 +66,38 @@ async def get_tasks_with_detail(session: AsyncSession = Depends(db.get_session))
65
66
return tasks
66
67
```
67
68
69
+ 访问:
70
+ - ` http://localhost:8000/sample_1/tasks-with-detail `
71
+
72
+ 可以看到 user 信息被添加了进来.
73
+ ``` json
74
+ [
75
+ {
76
+ "id" : 1 ,
77
+ "name" : " mvp tech design" ,
78
+ "owner_id" : 2 ,
79
+ "story_id" : 1 ,
80
+ "user" : {
81
+ "id" : 2 ,
82
+ "name" : " Eric" ,
83
+ "level" : " junior"
84
+ }
85
+ },
86
+ {
87
+ "id" : 2 ,
88
+ "name" : " implementation" ,
89
+ "owner_id" : 2 ,
90
+ "story_id" : 1 ,
91
+ "user" : {
92
+ "id" : 2 ,
93
+ "name" : " Eric" ,
94
+ "level" : " junior"
95
+ }
96
+ }
97
+ ]
98
+ ```
99
+
100
+
68
101
## 多层嵌套列表
69
102
70
103
使用相同的方式, 我们从 ` tasks-with-details ` 逐步构建到了 ` teams-with-details ` . 虽然是层层嵌套,但定义的方式非常简单。
@@ -89,6 +122,51 @@ class Sample1TeamDetail(tms.Team):
89
122
return loader.load(self .id)
90
123
```
91
124
125
+ 访问: ` http://localhost:8000/sample_1/teams-with-detail `
126
+
127
+
128
+ ` get_teams_with_detail_2 ` 描述了另一种场景, 假如我们利用了一些 ORM 的外键查询, 提前获取到了 team + sprints 级别的数据, 拿我可以在这个数据的基础上继续 resolve.
129
+
130
+ 输入数据:
131
+
132
+ ``` python
133
+ teams = [{
134
+ " id" : 1 ,
135
+ " name" : " team-A" ,
136
+ " sprints" : [
137
+ {
138
+ " id" : 1 ,
139
+ " name" : " Sprint A W1" ,
140
+ " status" : " close" ,
141
+ " team_id" : 1
142
+ },
143
+ {
144
+ " id" : 2 ,
145
+ " name" : " Sprint A W3" ,
146
+ " status" : " active" ,
147
+ " team_id" : 1
148
+ },
149
+ {
150
+ " id" : 3 ,
151
+ " name" : " Sprint A W5" ,
152
+ " status" : " plan" ,
153
+ " team_id" : 1
154
+ }
155
+ ]
156
+ }]
157
+ ```
158
+
159
+ 转换类型, 可以看到此处没有了 ` resolve_sprints ` . 但 sprints 数据转换成 ` Simple1SprintDetail ` 类型之后, 会自动继续扩展获取定义的关联类型.
160
+
161
+ ``` python
162
+ class Sample1TeamDetail2 (tms .Team ):
163
+ sprints: list[Sample1SprintDetail] = []
164
+
165
+ members: list[us.User] = []
166
+ def resolve_members (self , loader = LoaderDepend(ul.team_to_user_loader)):
167
+ return loader.load(self .id)
168
+ ```
169
+
92
170
## Dataloader 的使用
93
171
94
172
Dataloader 的作用收集完所有要查询的 parent_ids 之后,一次性查询到所有的 childrent 对象,接着根据 child 的 parent_id 聚合起来。
0 commit comments