fastgenerateapi 0.0.23__py2.py3-none-any.whl → 0.0.26__py2.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.
Potentially problematic release.
This version of fastgenerateapi might be problematic. Click here for more details.
- fastgenerateapi/__init__.py +1 -0
- fastgenerateapi/__version__.py +1 -1
- fastgenerateapi/api_view/base_view.py +29 -14
- fastgenerateapi/api_view/delete_filter_view.py +67 -0
- fastgenerateapi/api_view/delete_tree_view.py +13 -11
- fastgenerateapi/api_view/delete_view.py +12 -11
- fastgenerateapi/api_view/get_tree_view.py +2 -1
- fastgenerateapi/api_view/mixin/base_mixin.py +1 -1
- fastgenerateapi/api_view/mixin/dbmodel_mixin.py +5 -3
- fastgenerateapi/channel/consumer.py +5 -0
- fastgenerateapi/channel/websocket_view.py +3 -0
- fastgenerateapi/controller/ws_controller.py +8 -6
- fastgenerateapi/example/models.py +19 -11
- fastgenerateapi/example/routers.py +3 -3
- fastgenerateapi/example/schemas.py +1 -1
- fastgenerateapi/example/views.py +46 -10
- fastgenerateapi/my_fields/__init__.py +4 -0
- fastgenerateapi/my_fields/aes_field.py +166 -0
- fastgenerateapi/my_fields/enum_field.py +215 -0
- fastgenerateapi/my_fields/pk_field.py +68 -0
- fastgenerateapi/my_fields/pwd_field.py +81 -0
- fastgenerateapi/my_fields/soft_delete_field.py +54 -0
- fastgenerateapi/pydantic_utils/base_model.py +3 -1
- fastgenerateapi/schemas_factory/common_function.py +2 -1
- fastgenerateapi/schemas_factory/common_schema_factory.py +1 -1
- fastgenerateapi/schemas_factory/create_schema_factory.py +9 -3
- fastgenerateapi/schemas_factory/get_all_schema_factory.py +7 -0
- fastgenerateapi/schemas_factory/get_one_schema_factory.py +8 -0
- fastgenerateapi/schemas_factory/get_tree_schema_factory.py +8 -0
- fastgenerateapi/schemas_factory/update_schema_factory.py +9 -3
- fastgenerateapi/settings/settings.py +13 -7
- fastgenerateapi/utils/aes.py +93 -0
- fastgenerateapi/utils/snowflake.py +148 -0
- {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.26.dist-info}/METADATA +1 -1
- {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.26.dist-info}/RECORD +38 -29
- {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.26.dist-info}/WHEEL +1 -1
- {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.26.dist-info}/LICENSE +0 -0
- {fastgenerateapi-0.0.23.dist-info → fastgenerateapi-0.0.26.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
Dict,
|
|
7
|
+
Generic,
|
|
8
|
+
List,
|
|
9
|
+
Optional,
|
|
10
|
+
Tuple,
|
|
11
|
+
Type,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Union,
|
|
14
|
+
overload,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from fastapi import HTTPException
|
|
18
|
+
from tortoise.fields.data import IntEnumFieldInstance
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING: # pragma: nocoverage
|
|
21
|
+
from tortoise.models import Model
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class IntEnumField(IntEnumFieldInstance):
|
|
25
|
+
"""
|
|
26
|
+
传入参数数组,默认生成枚举类,数字从 1 开始
|
|
27
|
+
例如:
|
|
28
|
+
enum_list = ["A", "B"]
|
|
29
|
+
相当于枚举
|
|
30
|
+
class CategoryEnum(IntEnum):
|
|
31
|
+
one = 1
|
|
32
|
+
two = 2
|
|
33
|
+
通过方法 get_name 获取对应的值, "A"
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, enum_list: List[any], **kwargs: Any) -> None:
|
|
37
|
+
kwargs.setdefault("description", self.get_description(enum_list))
|
|
38
|
+
self.enum_list = enum_list
|
|
39
|
+
self.description = kwargs.get("description")
|
|
40
|
+
super().__init__(enum_type=self.create_enum(enum_list), **kwargs)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def constraints(self) -> dict:
|
|
44
|
+
return {
|
|
45
|
+
"ge": 0,
|
|
46
|
+
"le": len(self.enum_list),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
|
|
50
|
+
"""
|
|
51
|
+
Converts from the Python type to the DB type.
|
|
52
|
+
"""
|
|
53
|
+
if value is not None:
|
|
54
|
+
value = int(value) # pylint: disable=E1102
|
|
55
|
+
if value > len(self.enum_list) or 0 > value:
|
|
56
|
+
raise HTTPException(detail=f"枚举值:{value} 校验失败。【{self.description}】", status_code=422)
|
|
57
|
+
self.validate(value)
|
|
58
|
+
return value
|
|
59
|
+
|
|
60
|
+
def to_python_value(self, value: Any) -> Any:
|
|
61
|
+
"""
|
|
62
|
+
Converts from the DB type to the Python type.
|
|
63
|
+
"""
|
|
64
|
+
if value is not None:
|
|
65
|
+
value = int(value) # pylint: disable=E1102
|
|
66
|
+
self.validate(value)
|
|
67
|
+
return IntEnumClass(value, self.enum_list)
|
|
68
|
+
|
|
69
|
+
def get_description(self, enum_list):
|
|
70
|
+
description = ""
|
|
71
|
+
for index, val in enumerate(enum_list, 1):
|
|
72
|
+
description += f"{index}:{val};"
|
|
73
|
+
return description
|
|
74
|
+
|
|
75
|
+
def create_enum(self, enum_list):
|
|
76
|
+
# 创建枚举类的成员字典,确保值是唯一的
|
|
77
|
+
|
|
78
|
+
members = {self.number_to_words(name): name for name, _ in enumerate(enum_list, 1)}
|
|
79
|
+
|
|
80
|
+
# 使用Enum的元类EnumMeta来动态创建枚举类
|
|
81
|
+
enum_class = IntEnum("CategoryEnum", members)
|
|
82
|
+
|
|
83
|
+
# 返回创建的枚举类
|
|
84
|
+
return enum_class
|
|
85
|
+
|
|
86
|
+
def number_to_words(self, num):
|
|
87
|
+
# 定义数字到单词的映射
|
|
88
|
+
ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
|
|
89
|
+
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']
|
|
90
|
+
tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']
|
|
91
|
+
|
|
92
|
+
if num == 0:
|
|
93
|
+
return 'zero'
|
|
94
|
+
|
|
95
|
+
if num < 20:
|
|
96
|
+
return ones[num]
|
|
97
|
+
|
|
98
|
+
if num < 100:
|
|
99
|
+
ten_digit = num // 10
|
|
100
|
+
one_digit = num % 10
|
|
101
|
+
if one_digit == 0:
|
|
102
|
+
return tens[ten_digit]
|
|
103
|
+
else:
|
|
104
|
+
return tens[ten_digit] + '_' + ones[one_digit]
|
|
105
|
+
|
|
106
|
+
if num < 1000:
|
|
107
|
+
hundred_digit = num // 100
|
|
108
|
+
remaining = num % 100
|
|
109
|
+
return ones[hundred_digit] + '_hundred_' + self.number_to_words(remaining)
|
|
110
|
+
|
|
111
|
+
# 对于大于1000的数字,可以进一步扩展这个函数来处理
|
|
112
|
+
# 但请注意,标准的英文数字读法会变得复杂,涉及"thousand", "million", "billion"等词
|
|
113
|
+
|
|
114
|
+
# 这里仅处理到9999,如果需要处理更大的数字,请继续扩展这个函数
|
|
115
|
+
thousand_digit = num // 1000
|
|
116
|
+
remaining = num % 1000
|
|
117
|
+
return self.number_to_words(thousand_digit) + '_thousand_' + self.number_to_words(remaining)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class IntEnumClass:
|
|
121
|
+
def __init__(self, value: int, name_list: list):
|
|
122
|
+
self.value = value
|
|
123
|
+
self.name_list = name_list
|
|
124
|
+
|
|
125
|
+
def __str__(self):
|
|
126
|
+
return f"{self.value}"
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def name(self):
|
|
130
|
+
try:
|
|
131
|
+
return self.name_list[self.value-1]
|
|
132
|
+
except:
|
|
133
|
+
return self.value
|
|
134
|
+
|
|
135
|
+
def __add__(self, other: Union["IntEnumClass", int]):
|
|
136
|
+
if isinstance(other, IntEnumClass):
|
|
137
|
+
return IntEnumClass(self.value + other.value, self.name_list)
|
|
138
|
+
elif isinstance(other, int):
|
|
139
|
+
return IntEnumClass(self.value + other, self.name_list)
|
|
140
|
+
else:
|
|
141
|
+
raise TypeError(f"unsupported operand type(s) for +: 'NamedInt' and '{type(other).__name__}'")
|
|
142
|
+
|
|
143
|
+
def __sub__(self, other: Union["IntEnumClass", int]):
|
|
144
|
+
if isinstance(other, IntEnumClass):
|
|
145
|
+
return IntEnumClass(self.value - other.value, self.name_list)
|
|
146
|
+
elif isinstance(other, int):
|
|
147
|
+
return IntEnumClass(self.value - other, self.name_list)
|
|
148
|
+
else:
|
|
149
|
+
raise TypeError(f"unsupported operand type(s) for -: 'IntEnumClass' and '{type(other).__name__}'")
|
|
150
|
+
|
|
151
|
+
def __mul__(self, other: Union["IntEnumClass", int]):
|
|
152
|
+
if isinstance(other, IntEnumClass):
|
|
153
|
+
return IntEnumClass(self.value * other.value, self.name_list)
|
|
154
|
+
elif isinstance(other, int):
|
|
155
|
+
return IntEnumClass(self.value * other, self.name_list)
|
|
156
|
+
else:
|
|
157
|
+
raise TypeError(f"unsupported operand type(s) for *: 'IntEnumClass' and '{type(other).__name__}'")
|
|
158
|
+
|
|
159
|
+
def __truediv__(self, other: Union["IntEnumClass", int]):
|
|
160
|
+
if isinstance(other, IntEnumClass):
|
|
161
|
+
if other.value == 0:
|
|
162
|
+
raise ZeroDivisionError("division by zero")
|
|
163
|
+
return IntEnumClass(self.value // other.value, self.name_list)
|
|
164
|
+
elif isinstance(other, int):
|
|
165
|
+
if other == 0:
|
|
166
|
+
raise ZeroDivisionError("division by zero")
|
|
167
|
+
return IntEnumClass(self.value // other, self.name_list)
|
|
168
|
+
else:
|
|
169
|
+
raise TypeError(f"unsupported operand type(s) for /: 'IntEnumClass' and '{type(other).__name__}'")
|
|
170
|
+
|
|
171
|
+
def __eq__(self, other: Union["IntEnumClass", int]):
|
|
172
|
+
if isinstance(other, IntEnumClass):
|
|
173
|
+
return self.value == other.value
|
|
174
|
+
elif isinstance(other, int):
|
|
175
|
+
return self.value == other
|
|
176
|
+
return NotImplemented
|
|
177
|
+
|
|
178
|
+
def __ne__(self, other: Union["IntEnumClass", int]):
|
|
179
|
+
result = self.__eq__(other)
|
|
180
|
+
if result is NotImplemented:
|
|
181
|
+
return result
|
|
182
|
+
return not result
|
|
183
|
+
|
|
184
|
+
def __lt__(self, other: Union["IntEnumClass", int]):
|
|
185
|
+
if isinstance(other, IntEnumClass):
|
|
186
|
+
return self.value < other.value
|
|
187
|
+
elif isinstance(other, int):
|
|
188
|
+
return self.value < other
|
|
189
|
+
return NotImplemented
|
|
190
|
+
|
|
191
|
+
def __le__(self, other: Union["IntEnumClass", int]):
|
|
192
|
+
if isinstance(other, IntEnumClass):
|
|
193
|
+
return self.value <= other.value
|
|
194
|
+
elif isinstance(other, int):
|
|
195
|
+
return self.value <= other
|
|
196
|
+
return NotImplemented
|
|
197
|
+
|
|
198
|
+
def __gt__(self, other: Union["IntEnumClass", int]):
|
|
199
|
+
if isinstance(other, IntEnumClass):
|
|
200
|
+
return self.value > other.value
|
|
201
|
+
elif isinstance(other, int):
|
|
202
|
+
return self.value > other
|
|
203
|
+
return NotImplemented
|
|
204
|
+
|
|
205
|
+
def __ge__(self, other: Union["IntEnumClass", int]):
|
|
206
|
+
if isinstance(other, IntEnumClass):
|
|
207
|
+
return self.value >= other.value
|
|
208
|
+
elif isinstance(other, int):
|
|
209
|
+
return self.value >= other
|
|
210
|
+
return NotImplemented
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TYPE_CHECKING,
|
|
3
|
+
Any,
|
|
4
|
+
Callable,
|
|
5
|
+
Dict,
|
|
6
|
+
Generic,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
Type,
|
|
11
|
+
TypeVar,
|
|
12
|
+
Union,
|
|
13
|
+
overload,
|
|
14
|
+
)
|
|
15
|
+
from tortoise.fields import Field
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING: # pragma: nocoverage
|
|
18
|
+
from tortoise.models import Model
|
|
19
|
+
|
|
20
|
+
from fastgenerateapi.utils.snowflake import worker
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PrimaryKeyField(Field[str], str):
|
|
24
|
+
"""
|
|
25
|
+
Big integer field. (64-bit signed)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
SQL_TYPE = "BIGINT"
|
|
29
|
+
allows_generated = True
|
|
30
|
+
|
|
31
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
32
|
+
kwargs["generated"] = False
|
|
33
|
+
kwargs["unique"] = True
|
|
34
|
+
kwargs["description"] = "主键"
|
|
35
|
+
kwargs["default"] = worker.get_id
|
|
36
|
+
super().__init__(pk=True, **kwargs)
|
|
37
|
+
|
|
38
|
+
def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
|
|
39
|
+
"""
|
|
40
|
+
Converts from the Python type to the DB type.
|
|
41
|
+
"""
|
|
42
|
+
if value is not None:
|
|
43
|
+
value = int(value) # pylint: disable=E1102
|
|
44
|
+
self.validate(value)
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
def to_python_value(self, value: Any) -> Any:
|
|
48
|
+
"""
|
|
49
|
+
Converts from the DB type to the Python type.
|
|
50
|
+
"""
|
|
51
|
+
if value is not None:
|
|
52
|
+
value = str(value) # pylint: disable=E1102
|
|
53
|
+
self.validate(value)
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TYPE_CHECKING,
|
|
3
|
+
Any,
|
|
4
|
+
Callable,
|
|
5
|
+
Dict,
|
|
6
|
+
Generic,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
Type,
|
|
11
|
+
TypeVar,
|
|
12
|
+
Union,
|
|
13
|
+
overload,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from passlib.handlers.pbkdf2 import pbkdf2_sha256
|
|
17
|
+
from tortoise.fields import CharField
|
|
18
|
+
if TYPE_CHECKING: # pragma: nocoverage
|
|
19
|
+
from tortoise.models import Model
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PasswordField(CharField):
|
|
23
|
+
"""
|
|
24
|
+
Character field.
|
|
25
|
+
|
|
26
|
+
You must provide the following:
|
|
27
|
+
|
|
28
|
+
``salt`` (str):
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, salt: str, rounds=1000, **kwargs: Any) -> None:
|
|
32
|
+
kwargs.setdefault("max_length", 255)
|
|
33
|
+
kwargs.setdefault("description", "密码")
|
|
34
|
+
self.salt = salt
|
|
35
|
+
self.rounds = rounds
|
|
36
|
+
super().__init__(**kwargs)
|
|
37
|
+
|
|
38
|
+
def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
|
|
39
|
+
"""
|
|
40
|
+
Converts from the Python type to the DB type.
|
|
41
|
+
"""
|
|
42
|
+
if value is not None:
|
|
43
|
+
if not isinstance(value, self.field_type):
|
|
44
|
+
value = self.field_type(value) # pylint: disable=E1102
|
|
45
|
+
custom_pbkdf2 = pbkdf2_sha256.using(salt=self.salt.encode('utf-8'), rounds=self.rounds)
|
|
46
|
+
value = custom_pbkdf2.hash(value)
|
|
47
|
+
|
|
48
|
+
self.validate(value)
|
|
49
|
+
return value
|
|
50
|
+
|
|
51
|
+
def to_python_value(self, value: Any) -> Any:
|
|
52
|
+
"""
|
|
53
|
+
Converts from the DB type to the Python type.
|
|
54
|
+
"""
|
|
55
|
+
if value is not None and not isinstance(value, self.field_type):
|
|
56
|
+
value = PasswordString(self.field_type(value), self.salt, self.rounds) # pylint: disable=E1102
|
|
57
|
+
self.validate(value)
|
|
58
|
+
return value
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class PasswordString(str):
|
|
62
|
+
def __init__(self, password, salt, rounds):
|
|
63
|
+
self.value = password
|
|
64
|
+
self.salt = salt
|
|
65
|
+
self.rounds = rounds
|
|
66
|
+
super().__init__()
|
|
67
|
+
|
|
68
|
+
def __str__(self):
|
|
69
|
+
return self.value
|
|
70
|
+
|
|
71
|
+
def check_valid(self, password: str) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
检查密码是否正确
|
|
74
|
+
"""
|
|
75
|
+
custom_pbkdf2 = pbkdf2_sha256.using(salt=self.salt.encode('utf-8'), rounds=self.rounds)
|
|
76
|
+
return self.password == custom_pbkdf2.hash(password)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TYPE_CHECKING,
|
|
3
|
+
Any,
|
|
4
|
+
Callable,
|
|
5
|
+
Dict,
|
|
6
|
+
Generic,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
Type,
|
|
11
|
+
TypeVar,
|
|
12
|
+
Union,
|
|
13
|
+
overload,
|
|
14
|
+
)
|
|
15
|
+
from tortoise.fields import IntField
|
|
16
|
+
if TYPE_CHECKING: # pragma: nocoverage
|
|
17
|
+
from tortoise.models import Model
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SoftDeleteField(IntField):
|
|
21
|
+
"""
|
|
22
|
+
Integer field. (64-bit signed)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
allows_generated = False
|
|
26
|
+
|
|
27
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
28
|
+
kwargs.setdefault("index", True)
|
|
29
|
+
kwargs.setdefault("null", True)
|
|
30
|
+
kwargs.setdefault("default", None)
|
|
31
|
+
kwargs.setdefault("description", "软删除")
|
|
32
|
+
super().__init__(**kwargs)
|
|
33
|
+
|
|
34
|
+
def to_db_value(self, value: Any, instance: "Union[Type[Model], Model]") -> Any:
|
|
35
|
+
"""
|
|
36
|
+
Converts from the Python type to the DB type.
|
|
37
|
+
"""
|
|
38
|
+
if value is not None and not isinstance(value, self.field_type):
|
|
39
|
+
value = self.field_type(value) # pylint: disable=E1102
|
|
40
|
+
if value == 0:
|
|
41
|
+
value = None
|
|
42
|
+
self.validate(value)
|
|
43
|
+
return value
|
|
44
|
+
|
|
45
|
+
def to_python_value(self, value: Any) -> Any:
|
|
46
|
+
"""
|
|
47
|
+
Converts from the DB type to the Python type.
|
|
48
|
+
"""
|
|
49
|
+
if value is not None and not isinstance(value, self.field_type):
|
|
50
|
+
value = self.field_type(value) # pylint: disable=E1102
|
|
51
|
+
self.validate(value)
|
|
52
|
+
return value
|
|
53
|
+
|
|
54
|
+
|
|
@@ -10,12 +10,14 @@ class BaseModel(PydanticBaseModel):
|
|
|
10
10
|
json_encoders = JSON_ENCODERS
|
|
11
11
|
extra = Extra.ignore
|
|
12
12
|
orm_mode = True
|
|
13
|
+
from_attributes = True
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Config(BaseConfig):
|
|
16
17
|
json_encoders = JSON_ENCODERS
|
|
17
18
|
extra = Extra.ignore
|
|
18
|
-
orm_mode = True
|
|
19
|
+
orm_mode = True # v1 版本
|
|
20
|
+
from_attributes = True # v2 版本
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class QueryConfig(BaseConfig):
|
|
@@ -63,7 +63,8 @@ def get_field_info_from_model_class(model_class: Type[Model], field: str, descri
|
|
|
63
63
|
return Optional[str], FieldInfo(default=None, description=f"{field}")
|
|
64
64
|
return get_field_info(value, description=description)
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
field_info = model_class._meta.fields_map.get(field.split("__", maxsplit=1)[0])
|
|
67
|
+
if field_info:
|
|
67
68
|
description += field_info.description or ""
|
|
68
69
|
|
|
69
70
|
model_class = DBModelMixin._get_foreign_key_relation_class(model_class, field.split("__", maxsplit=1)[0])
|
|
@@ -26,7 +26,7 @@ def common_schema_factory(
|
|
|
26
26
|
include_fields = set()
|
|
27
27
|
exclude_fields = set()
|
|
28
28
|
if exclude_readonly:
|
|
29
|
-
exclude_fields.update(["id", "is_active", "created_at", "modified_at", "updated_at"])
|
|
29
|
+
exclude_fields.update(["id", "is_active", "deleted_at", "created_at", "modified_at", "updated_at"])
|
|
30
30
|
if hasattr(model_class, "PydanticMeta"):
|
|
31
31
|
if hasattr(model_class.PydanticMeta, "include"):
|
|
32
32
|
include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.include)
|
|
@@ -34,10 +34,16 @@ def create_schema_factory(
|
|
|
34
34
|
include_fields.update(all_fields_info.keys())
|
|
35
35
|
if hasattr(model_class.PydanticMeta, "exclude"):
|
|
36
36
|
exclude_fields.update(model_class.PydanticMeta.exclude)
|
|
37
|
+
if hasattr(model_class.PydanticMeta, "save_include"):
|
|
38
|
+
save_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.save_include)
|
|
39
|
+
all_fields_info.update(save_include_fields_dict)
|
|
40
|
+
include_fields.update(save_include_fields_dict.keys())
|
|
37
41
|
if hasattr(model_class.PydanticMeta, "create_include"):
|
|
38
|
-
|
|
39
|
-
all_fields_info.update(
|
|
40
|
-
include_fields.update(
|
|
42
|
+
create_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.create_include)
|
|
43
|
+
all_fields_info.update(create_include_fields_dict)
|
|
44
|
+
include_fields.update(create_include_fields_dict.keys())
|
|
45
|
+
if hasattr(model_class.PydanticMeta, "save_exclude"):
|
|
46
|
+
exclude_fields.update(model_class.PydanticMeta.save_exclude)
|
|
41
47
|
if hasattr(model_class.PydanticMeta, "create_exclude"):
|
|
42
48
|
exclude_fields.update(model_class.PydanticMeta.create_exclude)
|
|
43
49
|
else:
|
|
@@ -40,6 +40,13 @@ def get_all_schema_factory(
|
|
|
40
40
|
if hasattr(model_class.PydanticMeta, "exclude"):
|
|
41
41
|
exclude_fields.update(model_class.PydanticMeta.exclude)
|
|
42
42
|
|
|
43
|
+
if hasattr(model_class.PydanticMeta, "get_include"):
|
|
44
|
+
get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
|
|
45
|
+
all_fields_info.update(get_include_fields_dict)
|
|
46
|
+
include_fields.update(get_include_fields_dict.keys())
|
|
47
|
+
if hasattr(model_class.PydanticMeta, "get_exclude"):
|
|
48
|
+
exclude_fields.update(model_class.PydanticMeta.get_exclude)
|
|
49
|
+
|
|
43
50
|
# get_all_include
|
|
44
51
|
if hasattr(model_class.PydanticMeta, "get_all_include"):
|
|
45
52
|
get_all_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_all_include)
|
|
@@ -33,6 +33,14 @@ def get_one_schema_factory(
|
|
|
33
33
|
include_fields.update(all_fields_info.keys())
|
|
34
34
|
if hasattr(model_class.PydanticMeta, "exclude"):
|
|
35
35
|
exclude_fields.update(model_class.PydanticMeta.exclude)
|
|
36
|
+
|
|
37
|
+
if hasattr(model_class.PydanticMeta, "get_include"):
|
|
38
|
+
get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
|
|
39
|
+
all_fields_info.update(get_include_fields_dict)
|
|
40
|
+
include_fields.update(get_include_fields_dict.keys())
|
|
41
|
+
if hasattr(model_class.PydanticMeta, "get_exclude"):
|
|
42
|
+
exclude_fields.update(model_class.PydanticMeta.get_exclude)
|
|
43
|
+
|
|
36
44
|
if hasattr(model_class.PydanticMeta, "get_one_include"):
|
|
37
45
|
get_one_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_one_include)
|
|
38
46
|
all_fields_info.update(get_one_include_fields_dict)
|
|
@@ -38,6 +38,14 @@ def get_tree_schema_factory(
|
|
|
38
38
|
include_fields.update(include_fields_dict.keys())
|
|
39
39
|
else:
|
|
40
40
|
include_fields.update(all_fields_info.keys())
|
|
41
|
+
|
|
42
|
+
if hasattr(model_class.PydanticMeta, "get_include"):
|
|
43
|
+
get_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.get_include)
|
|
44
|
+
all_fields_info.update(get_include_fields_dict)
|
|
45
|
+
include_fields.update(get_include_fields_dict.keys())
|
|
46
|
+
if hasattr(model_class.PydanticMeta, "get_exclude"):
|
|
47
|
+
exclude_fields.update(model_class.PydanticMeta.get_exclude)
|
|
48
|
+
|
|
41
49
|
if hasattr(model_class.PydanticMeta, "get_tree_exclude"):
|
|
42
50
|
exclude_fields.update(model_class.PydanticMeta.get_tree_exclude)
|
|
43
51
|
if hasattr(model_class.PydanticMeta, "exclude"):
|
|
@@ -34,10 +34,16 @@ def update_schema_factory(
|
|
|
34
34
|
include_fields.update(all_fields_info.keys())
|
|
35
35
|
if hasattr(model_class.PydanticMeta, "exclude"):
|
|
36
36
|
exclude_fields.update(model_class.PydanticMeta.exclude)
|
|
37
|
+
if hasattr(model_class.PydanticMeta, "save_include"):
|
|
38
|
+
save_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.save_include)
|
|
39
|
+
all_fields_info.update(save_include_fields_dict)
|
|
40
|
+
include_fields.update(save_include_fields_dict.keys())
|
|
37
41
|
if hasattr(model_class.PydanticMeta, "update_include"):
|
|
38
|
-
|
|
39
|
-
all_fields_info.update(
|
|
40
|
-
include_fields.update(
|
|
42
|
+
update_include_fields_dict = get_dict_from_pydanticmeta(model_class, model_class.PydanticMeta.update_include)
|
|
43
|
+
all_fields_info.update(update_include_fields_dict)
|
|
44
|
+
include_fields.update(update_include_fields_dict.keys())
|
|
45
|
+
if hasattr(model_class.PydanticMeta, "save_exclude"):
|
|
46
|
+
exclude_fields.update(model_class.PydanticMeta.update_exclude)
|
|
41
47
|
if hasattr(model_class.PydanticMeta, "update_exclude"):
|
|
42
48
|
exclude_fields.update(model_class.PydanticMeta.update_exclude)
|
|
43
49
|
else:
|
|
@@ -29,12 +29,13 @@ class AppSettings(BaseSettings):
|
|
|
29
29
|
ROUTER_WHETHER_UNDERLINE_TO_STRIKE: Optional[bool] = Field(default=False, description="路由是否下划线转中划线")
|
|
30
30
|
ROUTER_WHETHER_ADD_SUFFIX: Optional[bool] = Field(default=True, description="增删改查路由是否添加后缀")
|
|
31
31
|
ROUTER_CREATE_SUFFIX_FIELD: Optional[str] = Field(default='create', description="创建路由后缀字段")
|
|
32
|
-
ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default='
|
|
33
|
-
ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default='
|
|
34
|
-
ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default='
|
|
32
|
+
ROUTER_GET_ONE_SUFFIX_FIELD: Optional[str] = Field(default='get_one', description="获取一个路由后缀字段")
|
|
33
|
+
ROUTER_GET_ALL_SUFFIX_FIELD: Optional[str] = Field(default='get_all', description="获取列表路由后缀字段")
|
|
34
|
+
ROUTER_GET_TREE_SUFFIX_FIELD: Optional[str] = Field(default='get_tree', description="获取树状数据路由后缀字段")
|
|
35
35
|
ROUTER_UPDATE_SUFFIX_FIELD: Optional[str] = Field(default='update', description="修改路由后缀字段")
|
|
36
36
|
ROUTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete', description="删除路由后缀字段")
|
|
37
|
-
ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='
|
|
37
|
+
ROUTER_RECURSION_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete_tree', description="递归删除路由后缀字段")
|
|
38
|
+
ROUTER_FILTER_DELETE_SUFFIX_FIELD: Optional[str] = Field(default='delete_filter', description="递归删除路由后缀字段")
|
|
38
39
|
|
|
39
40
|
# 函数转换路由时,默认添加字段,(遵循restful规范时,get路由处理方案)
|
|
40
41
|
RESTFUL_GET_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
|
|
@@ -46,9 +47,14 @@ class AppSettings(BaseSettings):
|
|
|
46
47
|
RESTFUL_DELETE_ROUTER_ADD_PREFIX: Optional[str] = Field(default='', description="函数转换路由时:前缀添加字段")
|
|
47
48
|
RESTFUL_DELETE_ROUTER_ADD_SUFFIX: Optional[str] = Field(default='', description="函数转换路由时:后缀pk前添加字段")
|
|
48
49
|
|
|
50
|
+
# 分布式id
|
|
51
|
+
WORKER_ID: Optional[int] = Field(default=1, description="数据中心(机器区域)ID")
|
|
52
|
+
DATACENTER_ID: Optional[int] = Field(default=1, description="机器ID")
|
|
53
|
+
|
|
49
54
|
# 数据库字段默认值
|
|
50
|
-
WHETHER_DELETE_FIELD: Optional[str] = Field(default="
|
|
51
|
-
|
|
55
|
+
WHETHER_DELETE_FIELD: Optional[str] = Field(default="deleted_at", description="是否删除字段;推荐命名 >> deleted_at;is_active")
|
|
56
|
+
DELETE_FIELD_TYPE: Optional[str] = Field(default="time", description="删除字段类型;推荐命名 >> time;bool")
|
|
57
|
+
# ACTIVE_DEFAULT_VALUE: Optional[bool] = Field(default=True, description="有效的默认值")
|
|
52
58
|
GET_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="查询结果是否排除有效字段")
|
|
53
59
|
CREATE_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="创建是否排除有效字段")
|
|
54
60
|
UPDATE_EXCLUDE_ACTIVE_VALUE: Optional[bool] = Field(default=True, description="修改是否排除有效字段")
|
|
@@ -70,7 +76,7 @@ class AppSettings(BaseSettings):
|
|
|
70
76
|
SCHEMAS_UNDERLINE_WHETHER_DOUBLE_TO_SINGLE: Optional[bool] = Field(default=True, description="序列化字段是否双下划线转单下划线")
|
|
71
77
|
|
|
72
78
|
class Config:
|
|
73
|
-
|
|
79
|
+
env_prefix = 'APP_'
|
|
74
80
|
env_file = "./.env"
|
|
75
81
|
case_sensitive = True
|
|
76
82
|
|