toms-fast 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toms_fast-0.2.1.dist-info/METADATA +467 -0
- toms_fast-0.2.1.dist-info/RECORD +60 -0
- toms_fast-0.2.1.dist-info/WHEEL +4 -0
- toms_fast-0.2.1.dist-info/entry_points.txt +2 -0
- tomskit/__init__.py +0 -0
- tomskit/celery/README.md +693 -0
- tomskit/celery/__init__.py +4 -0
- tomskit/celery/celery.py +306 -0
- tomskit/celery/config.py +377 -0
- tomskit/cli/__init__.py +207 -0
- tomskit/cli/__main__.py +8 -0
- tomskit/cli/scaffold.py +123 -0
- tomskit/cli/templates/__init__.py +42 -0
- tomskit/cli/templates/base.py +348 -0
- tomskit/cli/templates/celery.py +101 -0
- tomskit/cli/templates/extensions.py +213 -0
- tomskit/cli/templates/fastapi.py +400 -0
- tomskit/cli/templates/migrations.py +281 -0
- tomskit/cli/templates_config.py +122 -0
- tomskit/logger/README.md +466 -0
- tomskit/logger/__init__.py +4 -0
- tomskit/logger/config.py +106 -0
- tomskit/logger/logger.py +290 -0
- tomskit/py.typed +0 -0
- tomskit/redis/README.md +462 -0
- tomskit/redis/__init__.py +6 -0
- tomskit/redis/config.py +85 -0
- tomskit/redis/redis_pool.py +87 -0
- tomskit/redis/redis_sync.py +66 -0
- tomskit/server/__init__.py +47 -0
- tomskit/server/config.py +117 -0
- tomskit/server/exceptions.py +412 -0
- tomskit/server/middleware.py +371 -0
- tomskit/server/parser.py +312 -0
- tomskit/server/resource.py +464 -0
- tomskit/server/server.py +276 -0
- tomskit/server/type.py +263 -0
- tomskit/sqlalchemy/README.md +590 -0
- tomskit/sqlalchemy/__init__.py +20 -0
- tomskit/sqlalchemy/config.py +125 -0
- tomskit/sqlalchemy/database.py +125 -0
- tomskit/sqlalchemy/pagination.py +359 -0
- tomskit/sqlalchemy/property.py +19 -0
- tomskit/sqlalchemy/sqlalchemy.py +131 -0
- tomskit/sqlalchemy/types.py +32 -0
- tomskit/task/README.md +67 -0
- tomskit/task/__init__.py +4 -0
- tomskit/task/task_manager.py +124 -0
- tomskit/tools/README.md +63 -0
- tomskit/tools/__init__.py +18 -0
- tomskit/tools/config.py +70 -0
- tomskit/tools/warnings.py +37 -0
- tomskit/tools/woker.py +81 -0
- tomskit/utils/README.md +666 -0
- tomskit/utils/README_SERIALIZER.md +644 -0
- tomskit/utils/__init__.py +35 -0
- tomskit/utils/fields.py +434 -0
- tomskit/utils/marshal_utils.py +137 -0
- tomskit/utils/response_utils.py +13 -0
- tomskit/utils/serializers.py +447 -0
tomskit/utils/README.md
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
# Utils Module Guide
|
|
2
|
+
|
|
3
|
+
该模块提供了数据序列化、字段定义和响应处理等功能,支持异步数据源(如异步数据库查询)的数据序列化。
|
|
4
|
+
|
|
5
|
+
## 模块概述
|
|
6
|
+
|
|
7
|
+
Utils 模块基于 Flask-RESTful 的 `marshal` 函数改写的异步版本,适应 FastAPI 的异步环境,保持了相同的 API 设计和使用方式。主要特性包括:
|
|
8
|
+
|
|
9
|
+
- ⚡ **完全异步**:所有字段类和方法都支持异步操作,适配异步数据库查询
|
|
10
|
+
- 🔄 **灵活序列化**:支持单个对象、列表、元组和异步可迭代对象的序列化
|
|
11
|
+
- 🧩 **丰富字段类型**:提供多种字段类型,包括字符串、数字、日期时间、嵌套对象等
|
|
12
|
+
- 🎯 **装饰器支持**:提供装饰器自动序列化函数返回值
|
|
13
|
+
- 🔧 **属性映射**:支持字段的 `attribute` 属性映射,灵活处理数据源
|
|
14
|
+
|
|
15
|
+
**Import Path:**
|
|
16
|
+
```python
|
|
17
|
+
from tomskit.utils import (
|
|
18
|
+
marshal,
|
|
19
|
+
marshal_with,
|
|
20
|
+
marshal_with_field,
|
|
21
|
+
String,
|
|
22
|
+
DateTime,
|
|
23
|
+
Float,
|
|
24
|
+
Integer,
|
|
25
|
+
Nested,
|
|
26
|
+
List,
|
|
27
|
+
Raw,
|
|
28
|
+
Boolean,
|
|
29
|
+
FormattedString,
|
|
30
|
+
Arbitrary,
|
|
31
|
+
Fixed,
|
|
32
|
+
Price,
|
|
33
|
+
MarshallingException
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 核心类和函数
|
|
38
|
+
|
|
39
|
+
### marshal
|
|
40
|
+
|
|
41
|
+
数据序列化函数,根据字段定义序列化数据。支持单个对象、列表、元组和异步可迭代对象的序列化。
|
|
42
|
+
|
|
43
|
+
**注意:** 此函数是基于 Flask-RESTful 的 `marshal` 函数改写的异步版本,适应 FastAPI 的异步环境。
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
async def marshal(
|
|
47
|
+
data: Any,
|
|
48
|
+
fields: dict[str, Any],
|
|
49
|
+
envelope: str | None = None
|
|
50
|
+
) -> OrderedDict | list[OrderedDict]: ...
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**参数说明:**
|
|
54
|
+
- `data`: 要序列化的数据对象,可以是单个对象、列表、元组或异步可迭代对象
|
|
55
|
+
- `fields`: 字段定义字典,键为输出字段名,值为字段类型或嵌套字段字典
|
|
56
|
+
- `envelope`: 可选的包装键,用于将序列化结果包裹在指定的键下
|
|
57
|
+
|
|
58
|
+
**返回值:**
|
|
59
|
+
- 如果 `envelope` 为 `None`,返回 `OrderedDict` 或 `list[OrderedDict]`
|
|
60
|
+
- 如果 `envelope` 不为 `None`,返回包裹在指定键下的 `OrderedDict`
|
|
61
|
+
|
|
62
|
+
**功能特性:**
|
|
63
|
+
- 支持嵌套字段序列化(通过字典类型的字段值)
|
|
64
|
+
- 支持列表和元组的批量序列化
|
|
65
|
+
- 支持异步可迭代对象的序列化(`AsyncIterable`)
|
|
66
|
+
- 自动处理 `None` 值
|
|
67
|
+
- 支持字段的 `attribute` 属性映射
|
|
68
|
+
- 所有字段的 `output` 方法支持异步调用(`await`)
|
|
69
|
+
|
|
70
|
+
**使用示例:**
|
|
71
|
+
```python
|
|
72
|
+
from tomskit.utils import marshal, String, Integer, DateTime
|
|
73
|
+
|
|
74
|
+
# 定义字段
|
|
75
|
+
user_fields = {
|
|
76
|
+
'name': String(),
|
|
77
|
+
'age': Integer(),
|
|
78
|
+
'created_at': DateTime(dt_format='iso8601')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 序列化单个对象
|
|
82
|
+
user_data = {
|
|
83
|
+
'name': 'John',
|
|
84
|
+
'age': 30,
|
|
85
|
+
'created_at': datetime.now()
|
|
86
|
+
}
|
|
87
|
+
result = await marshal(user_data, user_fields)
|
|
88
|
+
# 输出: OrderedDict([('name', 'John'), ('age', 30), ('created_at', '2024-01-01T12:00:00')])
|
|
89
|
+
|
|
90
|
+
# 序列化列表
|
|
91
|
+
users = [user_data, {...}]
|
|
92
|
+
results = await marshal(users, user_fields)
|
|
93
|
+
# 输出: [OrderedDict(...), OrderedDict(...)]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### marshal_with
|
|
97
|
+
|
|
98
|
+
数据序列化装饰器,用于自动序列化函数返回值。基于 Flask-RESTful 的 `marshal_with` 装饰器改写的异步版本。
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
class marshal_with:
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
fields: dict[str, Any],
|
|
105
|
+
envelope: str | None = None
|
|
106
|
+
) -> None: ...
|
|
107
|
+
|
|
108
|
+
fields: dict[str, Any]
|
|
109
|
+
envelope: str | None
|
|
110
|
+
|
|
111
|
+
def __call__(self, f: Callable) -> Callable: ...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**参数说明:**
|
|
115
|
+
- `fields`: 字段定义字典,键为输出字段名,值为字段类型或嵌套字段字典
|
|
116
|
+
- `envelope`: 可选的包装键,用于将序列化结果包裹在指定的键下
|
|
117
|
+
|
|
118
|
+
**使用场景:**
|
|
119
|
+
- 装饰 API 路由处理函数,自动序列化返回值
|
|
120
|
+
- 支持复杂的嵌套字段结构
|
|
121
|
+
|
|
122
|
+
**功能特性:**
|
|
123
|
+
- 自动处理函数返回的元组 `(data, status_code, headers)`
|
|
124
|
+
- 支持 `JSONResponse` 直接返回
|
|
125
|
+
- 自动将序列化结果包装为 `JSONResponse`
|
|
126
|
+
- 使用 `marshal` 函数进行实际序列化
|
|
127
|
+
- 支持异步函数装饰
|
|
128
|
+
|
|
129
|
+
**使用示例:**
|
|
130
|
+
```python
|
|
131
|
+
from fastapi import FastAPI
|
|
132
|
+
from tomskit.utils import marshal_with, String, Integer
|
|
133
|
+
|
|
134
|
+
app = FastAPI()
|
|
135
|
+
|
|
136
|
+
user_fields = {
|
|
137
|
+
'name': String(),
|
|
138
|
+
'age': Integer()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@app.get("/users/{user_id}")
|
|
142
|
+
@marshal_with(user_fields)
|
|
143
|
+
async def get_user(user_id: int):
|
|
144
|
+
# 返回的数据会自动序列化
|
|
145
|
+
return {'name': 'John', 'age': 30}
|
|
146
|
+
# 或者返回元组 (data, status_code, headers)
|
|
147
|
+
# return {'name': 'John', 'age': 30}, 200, {'X-Custom': 'value'}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### marshal_with_field
|
|
151
|
+
|
|
152
|
+
单字段序列化装饰器,用于使用单个字段类型序列化函数返回值。基于 Flask-RESTful 的 `marshal_with_field` 装饰器改写的异步版本。
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
class marshal_with_field:
|
|
156
|
+
def __init__(
|
|
157
|
+
self,
|
|
158
|
+
field: type[Raw] | Raw
|
|
159
|
+
) -> None: ...
|
|
160
|
+
|
|
161
|
+
field: Raw
|
|
162
|
+
|
|
163
|
+
def __call__(self, f: Callable) -> Callable: ...
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**参数说明:**
|
|
167
|
+
- `field`: 字段类型或字段实例,可以是类型(如 `List(Integer)`)或实例(如 `List(Integer())`)
|
|
168
|
+
|
|
169
|
+
**使用场景:**
|
|
170
|
+
- 当只需要使用单个字段类型(如 `List`、`String` 等)序列化返回值时
|
|
171
|
+
- 适用于返回简单列表或单个值的函数
|
|
172
|
+
|
|
173
|
+
**功能特性:**
|
|
174
|
+
- 自动处理函数返回的元组 `(data, status_code, headers)`
|
|
175
|
+
- 支持 `JSONResponse` 直接返回
|
|
176
|
+
- 使用字段的 `format` 方法进行序列化
|
|
177
|
+
- 支持异步函数装饰
|
|
178
|
+
|
|
179
|
+
**使用示例:**
|
|
180
|
+
```python
|
|
181
|
+
from tomskit.utils import marshal_with_field, List, Integer
|
|
182
|
+
|
|
183
|
+
@app.get("/numbers")
|
|
184
|
+
@marshal_with_field(List(Integer))
|
|
185
|
+
async def get_numbers():
|
|
186
|
+
return ['1', 2, 3.0]
|
|
187
|
+
# 自动序列化为: [1, 2, 3]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 字段类
|
|
191
|
+
|
|
192
|
+
### Raw
|
|
193
|
+
|
|
194
|
+
字段基类,所有字段类型都继承自此类。
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
class Raw:
|
|
198
|
+
def __init__(
|
|
199
|
+
self,
|
|
200
|
+
default: Any = None,
|
|
201
|
+
attribute: str | None = None
|
|
202
|
+
) -> None: ...
|
|
203
|
+
|
|
204
|
+
attribute: str | None
|
|
205
|
+
default: Any
|
|
206
|
+
|
|
207
|
+
async def format(self, value: Any) -> Any: ...
|
|
208
|
+
|
|
209
|
+
async def output(self, key: str, obj: Any) -> Any: ...
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**参数说明:**
|
|
213
|
+
- `default`: 字段的默认值,如果未指定值,则使用该值
|
|
214
|
+
- `attribute`: 如果公开字段名与内部属性名不同,使用此参数指定内部属性名
|
|
215
|
+
|
|
216
|
+
### String
|
|
217
|
+
|
|
218
|
+
字符串字段,将值转换为字符串。
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
class String(Raw):
|
|
222
|
+
async def format(self, value: Any) -> str: ...
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**使用示例:**
|
|
226
|
+
```python
|
|
227
|
+
from tomskit.utils import String
|
|
228
|
+
|
|
229
|
+
field = String()
|
|
230
|
+
result = await field.format(123)
|
|
231
|
+
# 输出: "123"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Integer
|
|
235
|
+
|
|
236
|
+
整数字段,将值转换为整数。
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
class Integer(Raw):
|
|
240
|
+
def __init__(self, default: int = 0, **kwargs: Any) -> None: ...
|
|
241
|
+
|
|
242
|
+
async def format(self, value: Any) -> int: ...
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**参数说明:**
|
|
246
|
+
- `default`: 默认值,默认为 `0`
|
|
247
|
+
|
|
248
|
+
**使用示例:**
|
|
249
|
+
```python
|
|
250
|
+
from tomskit.utils import Integer
|
|
251
|
+
|
|
252
|
+
field = Integer(default=0)
|
|
253
|
+
result = await field.format("123")
|
|
254
|
+
# 输出: 123
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Float
|
|
258
|
+
|
|
259
|
+
浮点数字段,将值转换为浮点数。
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
class Float(Raw):
|
|
263
|
+
async def format(self, value: Any) -> float: ...
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Boolean
|
|
267
|
+
|
|
268
|
+
布尔字段,将值转换为布尔值。空集合(如 `""`、`{}`、`[]` 等)将被转换为 `False`。
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
class Boolean(Raw):
|
|
272
|
+
async def format(self, value: Any) -> bool: ...
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### DateTime
|
|
276
|
+
|
|
277
|
+
日期时间字段,支持 RFC 822 和 ISO 8601 格式。
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
class DateTime(Raw):
|
|
281
|
+
def __init__(
|
|
282
|
+
self,
|
|
283
|
+
dt_format: str = 'rfc822',
|
|
284
|
+
**kwargs: Any
|
|
285
|
+
) -> None: ...
|
|
286
|
+
|
|
287
|
+
dt_format: str
|
|
288
|
+
|
|
289
|
+
async def format(self, value: Any) -> str: ...
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**参数说明:**
|
|
293
|
+
- `dt_format`: 日期格式,可选值为 `'rfc822'` 或 `'iso8601'`,默认为 `'rfc822'`
|
|
294
|
+
|
|
295
|
+
**功能特性:**
|
|
296
|
+
- 支持 RFC 822 格式(如 "Sat, 01 Jan 2011 00:00:00 -0000")
|
|
297
|
+
- 支持 ISO 8601 格式(如 "2012-01-01T00:00:00")
|
|
298
|
+
- 自动转换为 UTC 时间
|
|
299
|
+
|
|
300
|
+
**使用示例:**
|
|
301
|
+
```python
|
|
302
|
+
from tomskit.utils import DateTime
|
|
303
|
+
from datetime import datetime
|
|
304
|
+
|
|
305
|
+
field = DateTime(dt_format='iso8601')
|
|
306
|
+
result = await field.format(datetime.now())
|
|
307
|
+
# 输出: "2024-01-01T12:00:00"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Nested
|
|
311
|
+
|
|
312
|
+
嵌套字段,用于嵌套对象序列化。
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
class Nested(Raw):
|
|
316
|
+
def __init__(
|
|
317
|
+
self,
|
|
318
|
+
nested: dict[str, Any],
|
|
319
|
+
allow_null: bool = False,
|
|
320
|
+
**kwargs: Any
|
|
321
|
+
) -> None: ...
|
|
322
|
+
|
|
323
|
+
nested: dict[str, Any]
|
|
324
|
+
allow_null: bool
|
|
325
|
+
|
|
326
|
+
async def output(self, key: str, obj: Any) -> dict[str, Any] | None: ...
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**参数说明:**
|
|
330
|
+
- `nested`: 嵌套字段定义字典
|
|
331
|
+
- `allow_null`: 如果嵌套对象为 `None`,是否返回 `None` 而不是空字典
|
|
332
|
+
|
|
333
|
+
**使用示例:**
|
|
334
|
+
```python
|
|
335
|
+
from tomskit.utils import Nested, String, Integer
|
|
336
|
+
|
|
337
|
+
user_fields = {
|
|
338
|
+
'name': String(),
|
|
339
|
+
'age': Integer()
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
profile_fields = {
|
|
343
|
+
'user': Nested(user_fields),
|
|
344
|
+
'bio': String()
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### List
|
|
349
|
+
|
|
350
|
+
列表字段,用于序列化列表数据。
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
class List(Raw):
|
|
354
|
+
def __init__(
|
|
355
|
+
self,
|
|
356
|
+
cls_or_instance: type[Raw] | Raw,
|
|
357
|
+
**kwargs: Any
|
|
358
|
+
) -> None: ...
|
|
359
|
+
|
|
360
|
+
container: Raw
|
|
361
|
+
|
|
362
|
+
async def format(self, value: Any) -> list[Any] | None: ...
|
|
363
|
+
|
|
364
|
+
async def output(self, key: str, data: Any) -> list[Any]: ...
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**使用示例:**
|
|
368
|
+
```python
|
|
369
|
+
from tomskit.utils import List, Integer, String
|
|
370
|
+
|
|
371
|
+
# 整数列表
|
|
372
|
+
field = List(Integer)
|
|
373
|
+
result = await field.format(['1', 2, 3.0])
|
|
374
|
+
# 输出: [1, 2, 3]
|
|
375
|
+
|
|
376
|
+
# 嵌套列表
|
|
377
|
+
nested_fields = {
|
|
378
|
+
'tags': List(String)
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### FormattedString
|
|
383
|
+
|
|
384
|
+
格式化字符串字段,支持从响应中插入其他值。
|
|
385
|
+
|
|
386
|
+
```python
|
|
387
|
+
class FormattedString(Raw):
|
|
388
|
+
def __init__(self, src_str: str) -> None: ...
|
|
389
|
+
|
|
390
|
+
src_str: str
|
|
391
|
+
|
|
392
|
+
async def output(self, key: str, obj: Any) -> str: ...
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**使用示例:**
|
|
396
|
+
```python
|
|
397
|
+
from tomskit.utils import FormattedString, String
|
|
398
|
+
|
|
399
|
+
fields = {
|
|
400
|
+
'name': String(),
|
|
401
|
+
'greeting': FormattedString("Hello {name}")
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
data = {'name': 'John'}
|
|
405
|
+
result = await marshal(data, fields)
|
|
406
|
+
# 输出: OrderedDict([('name', 'John'), ('greeting', 'Hello John')])
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Arbitrary
|
|
410
|
+
|
|
411
|
+
任意精度浮点数字段,用于处理大数值。
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
class Arbitrary(Raw):
|
|
415
|
+
async def format(self, value: Any) -> str: ...
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**功能特性:**
|
|
419
|
+
- 使用 `Decimal` 类型处理任意精度的浮点数
|
|
420
|
+
- 返回字符串格式的数值,避免精度丢失
|
|
421
|
+
- 适用于金融、科学计算等需要高精度的场景
|
|
422
|
+
|
|
423
|
+
**使用示例:**
|
|
424
|
+
```python
|
|
425
|
+
from tomskit.utils import Arbitrary
|
|
426
|
+
|
|
427
|
+
field = Arbitrary()
|
|
428
|
+
result = await field.format(634271127864378216478362784632784678324.23432)
|
|
429
|
+
# 输出: "634271127864378216478362784632784678324.23432"
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Fixed
|
|
433
|
+
|
|
434
|
+
固定精度数字字段,用于格式化小数位数。
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
class Fixed(Raw):
|
|
438
|
+
def __init__(
|
|
439
|
+
self,
|
|
440
|
+
decimals: int = 5,
|
|
441
|
+
**kwargs: Any
|
|
442
|
+
) -> None: ...
|
|
443
|
+
|
|
444
|
+
precision: Decimal
|
|
445
|
+
|
|
446
|
+
async def format(self, value: Any) -> str: ...
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**参数说明:**
|
|
450
|
+
- `decimals`: 小数位数,默认为 5
|
|
451
|
+
|
|
452
|
+
**功能特性:**
|
|
453
|
+
- 使用 `Decimal` 类型进行精确的数值计算
|
|
454
|
+
- 自动四舍五入到指定的小数位数
|
|
455
|
+
- 返回字符串格式的数值
|
|
456
|
+
|
|
457
|
+
**使用示例:**
|
|
458
|
+
```python
|
|
459
|
+
from tomskit.utils import Fixed
|
|
460
|
+
|
|
461
|
+
field = Fixed(decimals=2)
|
|
462
|
+
result = await field.format(3.14159)
|
|
463
|
+
# 输出: "3.14"
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Price
|
|
467
|
+
|
|
468
|
+
价格字段,`Fixed` 的别名,专门用于价格格式化。
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
Price = Fixed
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**使用场景:**
|
|
475
|
+
- 商品价格格式化
|
|
476
|
+
- 货币金额显示
|
|
477
|
+
- 需要固定小数位数的数值
|
|
478
|
+
|
|
479
|
+
**使用示例:**
|
|
480
|
+
```python
|
|
481
|
+
from tomskit.utils import Price
|
|
482
|
+
|
|
483
|
+
field = Price(decimals=2)
|
|
484
|
+
result = await field.format(99.999)
|
|
485
|
+
# 输出: "100.00"
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### MarshallingException
|
|
489
|
+
|
|
490
|
+
序列化异常类,用于处理序列化过程中的错误。
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
class MarshallingException(Exception):
|
|
494
|
+
def __init__(self, underlying_exception: Exception) -> None: ...
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## 完整使用示例
|
|
498
|
+
|
|
499
|
+
### 基础序列化
|
|
500
|
+
|
|
501
|
+
```python
|
|
502
|
+
from tomskit.utils import marshal, String, Integer, DateTime
|
|
503
|
+
from datetime import datetime
|
|
504
|
+
|
|
505
|
+
# 定义字段
|
|
506
|
+
user_fields = {
|
|
507
|
+
'name': String(),
|
|
508
|
+
'age': Integer(),
|
|
509
|
+
'created_at': DateTime(dt_format='iso8601')
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
# 数据
|
|
513
|
+
user_data = {
|
|
514
|
+
'name': 'John Doe',
|
|
515
|
+
'age': 30,
|
|
516
|
+
'created_at': datetime.now()
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
# 序列化
|
|
520
|
+
result = await marshal(user_data, user_fields)
|
|
521
|
+
print(result)
|
|
522
|
+
# 输出: OrderedDict([('name', 'John Doe'), ('age', 30), ('created_at', '2024-01-01T12:00:00')])
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### 嵌套对象序列化
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
from tomskit.utils import marshal, String, Integer, Nested, List
|
|
529
|
+
|
|
530
|
+
# 定义嵌套字段
|
|
531
|
+
address_fields = {
|
|
532
|
+
'street': String(),
|
|
533
|
+
'city': String(),
|
|
534
|
+
'zipcode': String()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
user_fields = {
|
|
538
|
+
'name': String(),
|
|
539
|
+
'age': Integer(),
|
|
540
|
+
'address': Nested(address_fields),
|
|
541
|
+
'tags': List(String)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
# 数据
|
|
545
|
+
user_data = {
|
|
546
|
+
'name': 'John',
|
|
547
|
+
'age': 30,
|
|
548
|
+
'address': {
|
|
549
|
+
'street': '123 Main St',
|
|
550
|
+
'city': 'New York',
|
|
551
|
+
'zipcode': '10001'
|
|
552
|
+
},
|
|
553
|
+
'tags': ['developer', 'python']
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
# 序列化
|
|
557
|
+
result = await marshal(user_data, user_fields)
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### 使用装饰器
|
|
561
|
+
|
|
562
|
+
```python
|
|
563
|
+
from fastapi import FastAPI
|
|
564
|
+
from tomskit.utils import marshal_with, String, Integer, Nested, List
|
|
565
|
+
|
|
566
|
+
app = FastAPI()
|
|
567
|
+
|
|
568
|
+
# 定义字段
|
|
569
|
+
user_fields = {
|
|
570
|
+
'name': String(),
|
|
571
|
+
'age': Integer(),
|
|
572
|
+
'tags': List(String)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
@app.get("/users/{user_id}")
|
|
576
|
+
@marshal_with(user_fields)
|
|
577
|
+
async def get_user(user_id: int):
|
|
578
|
+
# 从数据库获取用户(异步)
|
|
579
|
+
user = await db.session.get(User, user_id)
|
|
580
|
+
return {
|
|
581
|
+
'name': user.name,
|
|
582
|
+
'age': user.age,
|
|
583
|
+
'tags': user.tags # 假设这是一个异步属性
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### 属性映射
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
from tomskit.utils import marshal, String, Integer
|
|
591
|
+
|
|
592
|
+
# 使用 attribute 参数映射不同的属性名
|
|
593
|
+
fields = {
|
|
594
|
+
'display_name': String(attribute='name'), # 输出字段名为 display_name,但从 name 属性获取
|
|
595
|
+
'years_old': Integer(attribute='age') # 输出字段名为 years_old,但从 age 属性获取
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
class User:
|
|
599
|
+
def __init__(self):
|
|
600
|
+
self.name = 'John'
|
|
601
|
+
self.age = 30
|
|
602
|
+
|
|
603
|
+
user = User()
|
|
604
|
+
result = await marshal(user, fields)
|
|
605
|
+
# 输出: OrderedDict([('display_name', 'John'), ('years_old', 30)])
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### 异步数据源
|
|
609
|
+
|
|
610
|
+
```python
|
|
611
|
+
from tomskit.utils import marshal, String, Integer
|
|
612
|
+
from tomskit.sqlalchemy import db
|
|
613
|
+
|
|
614
|
+
# 定义字段
|
|
615
|
+
user_fields = {
|
|
616
|
+
'name': String(),
|
|
617
|
+
'age': Integer()
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
# 从异步数据库查询获取数据
|
|
621
|
+
async def get_users():
|
|
622
|
+
result = await db.session.execute(db.select(User))
|
|
623
|
+
users = result.scalars().all()
|
|
624
|
+
return await marshal(users, user_fields)
|
|
625
|
+
|
|
626
|
+
# 或者使用异步可迭代对象
|
|
627
|
+
async def get_users_stream():
|
|
628
|
+
async for user in db.session.stream(db.select(User)):
|
|
629
|
+
yield user
|
|
630
|
+
|
|
631
|
+
# 序列化异步可迭代对象
|
|
632
|
+
async def serialize_users():
|
|
633
|
+
users_stream = get_users_stream()
|
|
634
|
+
return await marshal(users_stream, user_fields)
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 使用 envelope 包装
|
|
638
|
+
|
|
639
|
+
```python
|
|
640
|
+
from tomskit.utils import marshal, String, Integer
|
|
641
|
+
|
|
642
|
+
fields = {
|
|
643
|
+
'name': String(),
|
|
644
|
+
'age': Integer()
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
data = {'name': 'John', 'age': 30}
|
|
648
|
+
|
|
649
|
+
# 使用 envelope 包装结果
|
|
650
|
+
result = await marshal(data, fields, envelope='user')
|
|
651
|
+
# 输出: OrderedDict([('user', OrderedDict([('name', 'John'), ('age', 30)]))])
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
## 注意事项
|
|
655
|
+
|
|
656
|
+
1. **异步操作**:所有字段类的方法都是异步的,需要使用 `await` 关键字
|
|
657
|
+
2. **数据源支持**:支持从异步数据源(如异步数据库查询)获取数据,会自动处理协程对象
|
|
658
|
+
3. **None 值处理**:自动处理 `None` 值,可以使用 `default` 参数指定默认值
|
|
659
|
+
4. **属性映射**:使用 `attribute` 参数可以映射不同的属性名
|
|
660
|
+
5. **嵌套序列化**:支持多层嵌套的对象序列化
|
|
661
|
+
6. **性能考虑**:对于大量数据的序列化,建议使用异步可迭代对象以提高性能
|
|
662
|
+
|
|
663
|
+
## 相关文档
|
|
664
|
+
|
|
665
|
+
- [Utils Guide](../docs/specs/utils_guide.md) - 详细的工具模块使用指南
|
|
666
|
+
- [Flask-RESTful 文档](https://flask-restful.readthedocs.io/) - Flask-RESTful 原始实现参考
|