fastgenerateapi 0.0.28__py2.py3-none-any.whl → 1.1.6__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.

Files changed (80) hide show
  1. fastgenerateapi/__init__.py +2 -2
  2. fastgenerateapi/__version__.py +1 -1
  3. fastgenerateapi/api_view/base_view.py +17 -7
  4. fastgenerateapi/api_view/create_view.py +1 -1
  5. fastgenerateapi/api_view/delete_filter_view.py +1 -1
  6. fastgenerateapi/api_view/delete_tree_view.py +3 -3
  7. fastgenerateapi/api_view/delete_view.py +3 -3
  8. fastgenerateapi/api_view/get_all_view.py +10 -8
  9. fastgenerateapi/api_view/get_one_view.py +1 -1
  10. fastgenerateapi/api_view/get_relation_view.py +1 -1
  11. fastgenerateapi/api_view/get_tree_view.py +1 -1
  12. fastgenerateapi/api_view/mixin/base_mixin.py +11 -7
  13. fastgenerateapi/api_view/mixin/dbmodel_mixin.py +30 -20
  14. fastgenerateapi/api_view/mixin/response_mixin.py +68 -38
  15. fastgenerateapi/api_view/mixin/tool_mixin.py +1 -357
  16. fastgenerateapi/api_view/mixin/utils/__init__.py +0 -0
  17. fastgenerateapi/api_view/mixin/utils/docx_util.py +399 -0
  18. fastgenerateapi/api_view/mixin/utils/file_util.py +30 -0
  19. fastgenerateapi/api_view/mixin/utils/pdf_util.py +76 -0
  20. fastgenerateapi/api_view/mixin/utils/xlsx_util.py +336 -0
  21. fastgenerateapi/api_view/mixin/utils/zip_util.py +50 -0
  22. fastgenerateapi/api_view/switch_view.py +2 -2
  23. fastgenerateapi/api_view/update_relation_view.py +3 -3
  24. fastgenerateapi/api_view/update_view.py +1 -1
  25. fastgenerateapi/cache/cache_decorator.py +1 -1
  26. fastgenerateapi/controller/filter_controller.py +68 -26
  27. fastgenerateapi/controller/router_controller.py +9 -9
  28. fastgenerateapi/controller/rpc_controller.py +1 -1
  29. fastgenerateapi/controller/ws_controller.py +1 -1
  30. fastgenerateapi/deps/filter_params_deps.py +34 -4
  31. fastgenerateapi/deps/paginator_deps.py +4 -4
  32. fastgenerateapi/deps/tree_params_deps.py +4 -4
  33. fastgenerateapi/fastapi_utils/__init__.py +0 -0
  34. fastgenerateapi/fastapi_utils/all.py +5 -0
  35. fastgenerateapi/fastapi_utils/param_utils.py +37 -0
  36. fastgenerateapi/fastapi_utils/response_utils.py +344 -0
  37. fastgenerateapi/model/__init__.py +0 -0
  38. fastgenerateapi/model/base_model.py +56 -0
  39. fastgenerateapi/my_fields/enum_field.py +5 -5
  40. fastgenerateapi/my_fields/validator.py +60 -0
  41. fastgenerateapi/pydantic_utils/base_model.py +46 -20
  42. fastgenerateapi/pydantic_utils/base_settings.py +16 -0
  43. fastgenerateapi/pydantic_utils/json_encoders.py +2 -1
  44. fastgenerateapi/schemas_factory/common_function.py +1 -1
  45. fastgenerateapi/schemas_factory/common_schema_factory.py +4 -4
  46. fastgenerateapi/schemas_factory/create_schema_factory.py +4 -4
  47. fastgenerateapi/schemas_factory/filter_schema_factory.py +6 -6
  48. fastgenerateapi/schemas_factory/get_all_schema_factory.py +5 -5
  49. fastgenerateapi/schemas_factory/get_one_schema_factory.py +4 -3
  50. fastgenerateapi/schemas_factory/get_relation_schema_factory.py +3 -3
  51. fastgenerateapi/schemas_factory/get_tree_schema_factory.py +3 -3
  52. fastgenerateapi/schemas_factory/response_factory.py +3 -3
  53. fastgenerateapi/schemas_factory/sql_get_all_schema_factory.py +3 -3
  54. fastgenerateapi/schemas_factory/update_schema_factory.py +4 -4
  55. fastgenerateapi/settings/__init__.py +6 -0
  56. fastgenerateapi/settings/all_settings.py +91 -0
  57. fastgenerateapi/settings/{settings.py → app_settings.py} +9 -9
  58. fastgenerateapi/settings/db_settings.py +69 -0
  59. fastgenerateapi/settings/file_settings.py +24 -0
  60. fastgenerateapi/settings/jwt_settings.py +23 -0
  61. fastgenerateapi/settings/otlp_settings.py +69 -0
  62. fastgenerateapi/settings/redis_settings.py +16 -0
  63. fastgenerateapi/settings/sms_settings.py +25 -0
  64. fastgenerateapi/settings/system_settings.py +30 -0
  65. fastgenerateapi/utils/auto_discover.py +61 -0
  66. fastgenerateapi/utils/file_utils.py +76 -0
  67. fastgenerateapi/utils/pwd_utils.py +49 -0
  68. fastgenerateapi/utils/ramdom_utils.py +48 -0
  69. fastgenerateapi/utils/snowflake.py +23 -20
  70. fastgenerateapi/utils/str_util.py +120 -0
  71. fastgenerateapi/utils/swagger_to_js.py +26 -0
  72. {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.6.dist-info}/METADATA +61 -24
  73. fastgenerateapi-1.1.6.dist-info/RECORD +109 -0
  74. {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.6.dist-info}/WHEEL +1 -1
  75. {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.6.dist-info}/top_level.txt +1 -0
  76. script/__init__.py +2 -0
  77. fastgenerateapi/settings/register_settings.py +0 -6
  78. fastgenerateapi/utils/parse_str.py +0 -36
  79. fastgenerateapi-0.0.28.dist-info/RECORD +0 -82
  80. {fastgenerateapi-0.0.28.dist-info → fastgenerateapi-1.1.6.dist-info}/LICENSE +0 -0
@@ -1,23 +1,3 @@
1
- import importlib
2
- import io
3
- import operator
4
- import os
5
- import time
6
- import uuid
7
- from pathlib import Path
8
- from typing import List, Union, Dict, Type, Tuple, Optional
9
-
10
- from fastapi import UploadFile
11
- from pydantic import create_model, ValidationError, BaseModel
12
- from starlette._utils import is_async_callable
13
- from starlette.background import BackgroundTask
14
- from starlette.responses import StreamingResponse, FileResponse
15
- from tortoise.models import Model
16
- from tortoise.queryset import QuerySetSingle
17
-
18
- from fastgenerateapi.schemas_factory.common_schema_factory import common_schema_factory
19
-
20
-
21
1
  class ToolMixin:
22
2
 
23
3
  @staticmethod
@@ -26,346 +6,10 @@ class ToolMixin:
26
6
  字典key,value互转
27
7
  """
28
8
  result = {}
29
- for key, val in data:
9
+ for key, val in data.items():
30
10
  result[val] = key
31
11
  return result
32
12
 
33
- async def export_xlsx(
34
- self,
35
- model_list: Model,
36
- headers: List[str],
37
- fields: List[str],
38
- fields_handler: dict,
39
- file_save_path: Optional[str] = None,
40
- # rpc_param: Union[Dict[str, Dict[str, List[str]]], Type[RPCParam], None] = None,
41
- title: str = None,
42
- modules: str = "openpyxl"
43
- ) -> StreamingResponse:
44
- limit_modules = ["openpyxl", "xlsxwriter"]
45
- if modules not in limit_modules:
46
- return self.error(msg=f"export xlsx modules only import {'、'.join(limit_modules)}")
47
- try:
48
- wb = importlib.import_module(modules).Workbook()
49
- except Exception:
50
- return self.error(msg=f"please pip install {modules}")
51
- if modules == "openpyxl":
52
- def write(sh, row, col, value):
53
- sh.cell(row, col).value = value
54
-
55
- start_col = 1
56
- start_row = 1
57
- else:
58
- def write(sh, row, col, value):
59
- sh.write(row, col, value)
60
-
61
- start_col = 0
62
- start_row = 0
63
- try:
64
- sh = wb.active
65
- sh.title = title if title else f'{self.model_class._meta.table_description}'
66
-
67
- for col, header in enumerate(headers, start_col):
68
- write(sh, start_row, col, header)
69
-
70
- for row, model in enumerate(model_list, start_row + 1):
71
- model = await self.getattr_model(model=model, fields=fields)
72
- # model = await self.setattr_model_rpc(self.model_class, model, rpc_param)
73
-
74
- for col, field in enumerate(fields, 1):
75
- info = getattr(model, field, "")
76
- handler = fields_handler.get(field)
77
- if handler and hasattr(handler, "__call__"):
78
- if is_async_callable(handler):
79
- info = await handler(info)
80
- else:
81
- info = handler(info)
82
- write(sh, row, col, info)
83
- finally:
84
- if file_save_path:
85
- wb.save(file_save_path)
86
- return self.success(msg="请求成功")
87
- bytes_io = io.BytesIO()
88
- wb.save(bytes_io)
89
- bytes_io.seek(0)
90
-
91
- return StreamingResponse(
92
- bytes_io,
93
- media_type="application/vnd.ms-excel;charset=UTF-8",
94
- )
95
-
96
- async def export_pdf(
97
- self,
98
- model: Model,
99
- fields_list: List[List[Union[str, Tuple[str]]]],
100
- data: List[List[str]],
101
- # rpc_param: Union[Dict[str, Dict[str, List[str]]], Type[RPCParam], None] = None,
102
- font: str = "msyh",
103
- font_path: str = None,
104
- modules: str = "fpdf"
105
- ) -> StreamingResponse:
106
- """
107
- fields_list: [["名字", ("name", "名字"), (数据库字段, 字段中文名)], [第二行]]
108
-
109
- """
110
- limit_modules = ["fpdf"]
111
- if modules not in limit_modules:
112
- return self.error(msg=f"export xlsx modules only import {'、'.join(limit_modules)}")
113
- try:
114
- pdf = importlib.import_module(modules).FPDF()
115
- except Exception:
116
- return self.error(msg=f"please pip install {modules}")
117
- pdf.add_page()
118
- pdf.add_font(font, '', font_path if font_path else f"../font/{font}.ttf", True)
119
- pdf.set_font(font, '', 8)
120
- if data:
121
- for data_row in data:
122
- data_row_width = 180 / len(data_row)
123
- for data_col in data_row:
124
- pdf.cell(data_row_width, 8, data_col)
125
- pdf.ln(10)
126
- else:
127
- async def write(model_single_obj):
128
- fields_data = []
129
- for fields in fields_list:
130
- for field in fields:
131
- if type(field) == tuple:
132
- fields_data.append(field[0])
133
- model_data = await self.getattr_model(model_single_obj, fields_data)
134
- # model_data = await self.setattr_model_rpc(self.model_class, model_data, rpc_param)
135
- for fields in fields_list:
136
- cell_width = 180 / len(fields)
137
- for field in fields:
138
- if type(field) == str:
139
- msg = f"{field[1]}"
140
- else:
141
- msg = f"{field[1]} {getattr(model_data, field[0]) if getattr(model_data, field[0]) else ''}"
142
- pdf.cell(cell_width, 8, msg)
143
- pdf.ln(10)
144
-
145
- if type(model) == QuerySetSingle:
146
- await write(model)
147
- else:
148
- for model_obj in model:
149
- await write(model_obj)
150
- pdf.add_page()
151
- byte_string = pdf.output(dest="S").encode('latin-1')
152
- bytes_io = io.BytesIO(byte_string)
153
-
154
- return StreamingResponse(
155
- bytes_io,
156
- media_type="application/pdf"
157
- )
158
-
159
- async def import_xlsx(
160
- self,
161
- file: UploadFile,
162
- file_save_path: str,
163
- headers: List[str],
164
- # [
165
- # "name",
166
- # ("is_male", {"男": True, "女": False} 或者 方法, {"额外字段": 方法}, ...),
167
- # ]
168
- # 方法(默认传excel的值)
169
- fields: List[Union[str, dict, tuple, list]],
170
- combine_fields: Optional[List[Dict[str, any]]] = None,
171
- model_class: Optional[Type[Model]] = None,
172
- create_schema: Optional[Type[BaseModel]] = None,
173
- # storage_path: Union[str, Path],
174
- # rpc_param: Union[Dict[str, Dict[str, List[Union[str, tuple]]]], Type[RPCParam]] = None,
175
- is_delete: Optional[bool] = True,
176
- modules: str = "openpyxl",
177
- ) -> StreamingResponse:
178
- """
179
- fields: 方法(默认传excel的值)
180
- 例如:
181
- [
182
- "name", # 传入值是 name 字段的值
183
- ("is_male", {"男": True, "女": False} 或者 方法, {"额外字段": 方法}, ...),
184
- # 值 "男" 获取为bool值,不在字典里为None, 页可以自定义 同步或异步方法 获取值
185
- ]
186
- """
187
- limit_modules = ["openpyxl"]
188
- if modules not in limit_modules:
189
- return self.error(msg=f"export xlsx modules only import {'、'.join(limit_modules)}")
190
-
191
- if not file:
192
- return self.fail(msg=f"请先选择合适的文件")
193
-
194
- if not model_class:
195
- model_class = self.model_class
196
- if not create_schema:
197
- create_schema = common_schema_factory(model_class, name=f"{model_class.__name__}ExcelImportSchema")
198
- res = await file.read()
199
- with open(file_save_path, 'wb') as destination:
200
- destination.write(res)
201
- try:
202
- wb = importlib.import_module(modules).load_workbook(file_save_path, read_only=True, data_only=True)
203
- except Exception:
204
- return self.error(msg=f"please pip install {modules}")
205
- try:
206
- ws = wb.active
207
-
208
- header_row = ws[1]
209
- header_list = []
210
- for msg in header_row:
211
- header_list.append(str(msg.value).replace(" ", ''))
212
-
213
- if len(header_list) != len(headers):
214
- return self.fail(message="文件首行长度校验错误")
215
-
216
- if not operator.eq(header_list, headers):
217
- return self.fail(message="文件首行内容校验错误")
218
-
219
- # if ws.max_row < 2:
220
- # return self.fail(msg="导入数据不能为空")
221
-
222
- create_list = []
223
- effective_row = 0
224
- for row in range(2, ws.max_row + 1):
225
- data = {}
226
- # data_schema = {}
227
- row_data = ws[row]
228
- if await self.excel_row_is_empty(row_data):
229
- continue
230
- effective_row += 1
231
- for col, field_input in enumerate(fields):
232
- if type(field_input) in [str, int]:
233
- data[field_input] = row_data[col].value
234
- # data_schema[field_input] = (type(row_data[col].value), ...)
235
-
236
- if type(field_input) == tuple or type(field_input) == list:
237
- key = field_input[0]
238
- val = field_input[1]
239
- required_doc = {}
240
- if len(field_input) > 2:
241
- required_doc = field_input[2]
242
- if required_doc == "required":
243
- required_doc = {"required": True}
244
- if type(val) == dict:
245
- model_val = val.get(row_data[col].value)
246
- if not model_val and required_doc.get("required"):
247
- return self.fail(
248
- msg=required_doc.get("error",
249
- "") or f"第{row}行{self.get_field_description(key)}不能为空")
250
- data[key] = model_val
251
- # data_schema[key] = (type(model_val), ...)
252
- elif hasattr(val, "__call__"):
253
- if is_async_callable(val):
254
- model_val = await val(row_data[col].value)
255
- else:
256
- model_val = val(row_data[col].value)
257
- data[key] = model_val
258
- # data_schema[key] = (type(model_val), ...)
259
- else:
260
- raise NotImplemented
261
- else:
262
- raise NotImplemented
263
- for combine_field in combine_fields:
264
- field = combine_field.get("field", None)
265
- value = combine_field.get("value", None)
266
- function = combine_field.get("function", None)
267
- args = combine_field.get("args", None)
268
- if not field or (not function and not value):
269
- continue
270
- if value:
271
- data[field] = value
272
- else:
273
- if not args:
274
- if is_async_callable(function):
275
- model_val = await function()
276
- else:
277
- model_val = function()
278
- else:
279
- args_list = []
280
- for arg in args:
281
- args_list.append(data.get(arg, ""))
282
- if is_async_callable(function):
283
- model_val = await function(*args_list)
284
- else:
285
- model_val = function(*args_list)
286
- data[field] = model_val
287
- try:
288
- create_obj = model_class(**create_schema(**data).dict(exclude_unset=True))
289
- except ValidationError as e:
290
- error_field = e.errors()[0].get('loc')[0]
291
- description = self.get_field_description(error_field)
292
- if not data.get(error_field):
293
- return self.fail(message=f"第{row}行{description}不能为空")
294
- return self.fail(message=f"第{row}行{description}填写错误")
295
- await self.check_unique_field(create_obj, model_class=model_class)
296
- create_list.append(create_obj)
297
-
298
- await model_class.bulk_create(create_list)
299
- finally:
300
- wb.close()
301
- if effective_row == 0:
302
- return self.fail(message="导入数据不能为空")
303
- if is_delete:
304
- return self.success(
305
- msg='创建成功',
306
- background=BackgroundTask(lambda: os.remove(file_save_path))
307
- )
308
- return self.success(msg='创建成功')
309
-
310
- @staticmethod
311
- async def excel_row_is_empty(row_list) -> bool:
312
- is_empty = True
313
- for row in row_list:
314
- if row.value is not None:
315
- return False
316
-
317
- return is_empty
318
-
319
- async def excel_model(
320
- self,
321
- headers: List[str] = None,
322
- model_class: Optional[Model] = None,
323
- excel_model_path: Optional[str] = None,
324
- modules: str = "openpyxl",
325
- title: Optional[str] = None,
326
- ) -> Union[FileResponse, StreamingResponse]:
327
- if excel_model_path:
328
- return FileResponse(
329
- path=excel_model_path,
330
- filename="导入模板.xlsx",
331
- media_type="xlsx",
332
- )
333
-
334
- limit_modules = ["openpyxl", "xlsxwriter"]
335
- if modules not in limit_modules:
336
- return self.error(msg=f"export xlsx modules only import {'、'.join(limit_modules)}")
337
- try:
338
- wb = importlib.import_module(modules).Workbook()
339
- except Exception:
340
- return self.error(msg=f"please pip install {modules}")
341
- if modules == "openpyxl":
342
- def write(sh, row, col, value):
343
- sh.cell(row, col).value = value
344
-
345
- start_col = 1
346
- start_row = 1
347
- else:
348
- def write(sh, row, col, value):
349
- sh.write(row, col, value)
350
-
351
- start_col = 0
352
- start_row = 0
353
- try:
354
- sh = wb.active
355
- sh.title = title if title else f'{model_class._meta.table_description}'
356
-
357
- for col, header in enumerate(headers, start_col):
358
- write(sh, start_row, col, header)
359
-
360
- finally:
361
- bytes_io = io.BytesIO()
362
- wb.save(bytes_io)
363
- bytes_io.seek(0)
364
-
365
- return StreamingResponse(
366
- bytes_io,
367
- media_type="application/vnd.ms-excel;charset=UTF-8",
368
- )
369
13
 
370
14
 
371
15
 
File without changes