基本完成文档
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import and_, func
|
||||
from applications.extensions import db
|
||||
|
||||
|
||||
class ModelFilter:
|
||||
"""
|
||||
ORM 多条件查询构造器,支持多种查询条件组合。
|
||||
ORM 多条件查询构造器,支持多种查询条件组合,自动转义特殊字符防止SQL注入。
|
||||
|
||||
示例:
|
||||
mf = ModelFilter()
|
||||
@@ -13,14 +13,14 @@ class ModelFilter:
|
||||
query = User.query.filter(mf.get_filter(User))
|
||||
"""
|
||||
filter_field = {} # 存储字段过滤条件
|
||||
filter_list = [] # 存储最终的过滤条件列表
|
||||
filter_list = [] # 存储最终的过滤条件列表
|
||||
|
||||
# 查询类型常量
|
||||
type_exact = "exact" # 精确匹配
|
||||
type_neq = "neq" # 不等于
|
||||
type_exact = "exact" # 精确匹配
|
||||
type_neq = "neq" # 不等于
|
||||
type_greater = "greater" # 大于
|
||||
type_less = "less" # 小于
|
||||
type_vague = "vague" # 模糊匹配
|
||||
type_less = "less" # 小于
|
||||
type_vague = "vague" # 模糊匹配
|
||||
type_contains = "contains" # 包含
|
||||
type_between = "between" # 范围查询
|
||||
|
||||
@@ -29,117 +29,145 @@ class ModelFilter:
|
||||
self.filter_field = {}
|
||||
self.filter_list = []
|
||||
|
||||
@staticmethod
|
||||
def escape_like(value: str, escape_char: str = '\\') -> str:
|
||||
"""
|
||||
转义LIKE查询中的特殊字符(%, _ 和转义字符本身)
|
||||
|
||||
:param value: 需要转义的原始字符串
|
||||
:param escape_char: 转义字符(默认反斜杠)
|
||||
:return: 转义后的安全字符串
|
||||
"""
|
||||
return (
|
||||
value.replace(escape_char, escape_char * 2)
|
||||
.replace('%', escape_char + '%')
|
||||
.replace('_', escape_char + '_')
|
||||
)
|
||||
|
||||
def exact(self, field_name, value):
|
||||
"""
|
||||
添加精确匹配条件。
|
||||
添加精确匹配条件(自动处理字符串类型参数)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 匹配的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 匹配的值(自动过滤空字符串)
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": value, "type": self.type_exact}
|
||||
if value is not None and value != '':
|
||||
# 字符串类型自动调用escape_like(防止特殊字符影响精确匹配)
|
||||
processed_value = self.escape_like(str(value)) if isinstance(value, str) else value
|
||||
self.filter_field[field_name] = {"data": processed_value, "type": self.type_exact}
|
||||
|
||||
def neq(self, field_name, value):
|
||||
"""
|
||||
添加不等于条件。
|
||||
添加不等于条件(自动处理字符串类型参数)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 不匹配的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 不匹配的值(自动过滤空字符串)
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": value, "type": self.type_neq}
|
||||
if value is not None and value != '':
|
||||
# 字符串类型自动调用escape_like
|
||||
processed_value = self.escape_like(str(value)) if isinstance(value, str) else value
|
||||
self.filter_field[field_name] = {"data": processed_value, "type": self.type_neq}
|
||||
|
||||
def greater(self, field_name, value):
|
||||
"""
|
||||
添加大于条件。
|
||||
添加大于条件(数值/日期比较)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 大于的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 比较的数值/日期
|
||||
"""
|
||||
if value and value != '':
|
||||
if value is not None and value != '':
|
||||
self.filter_field[field_name] = {"data": value, "type": self.type_greater}
|
||||
|
||||
def less(self, field_name, value):
|
||||
"""
|
||||
添加小于条件。
|
||||
添加小于条件(数值/日期比较)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 小于的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 比较的数值/日期
|
||||
"""
|
||||
if value and value != '':
|
||||
if value is not None and value != '':
|
||||
self.filter_field[field_name] = {"data": value, "type": self.type_less}
|
||||
|
||||
def vague(self, field_name, value: str):
|
||||
"""
|
||||
添加模糊匹配条件(左右模糊)。
|
||||
添加安全模糊匹配(自动转义特殊字符,左右加%)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 模糊匹配的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 需要模糊匹配的字符串(自动过滤空值)
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": ('%' + value + '%'), "type": self.type_vague}
|
||||
escaped_value = self.escape_like(value)
|
||||
self.filter_field[field_name] = {"data": f'%{escaped_value}%', "type": self.type_vague}
|
||||
|
||||
def left_vague(self, field_name, value: str):
|
||||
"""
|
||||
添加左模糊匹配条件。
|
||||
添加安全左模糊匹配(自动转义特殊字符,左侧加%)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 左模糊匹配的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 需要左模糊匹配的字符串
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": ('%' + value), "type": self.type_vague}
|
||||
escaped_value = self.escape_like(value)
|
||||
self.filter_field[field_name] = {"data": f'%{escaped_value}', "type": self.type_vague}
|
||||
|
||||
def right_vague(self, field_name, value: str):
|
||||
"""
|
||||
添加右模糊匹配条件。
|
||||
添加安全右模糊匹配(自动转义特殊字符,右侧加%)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 右模糊匹配的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 需要右模糊匹配的字符串
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": (value + '%'), "type": self.type_vague}
|
||||
escaped_value = self.escape_like(value)
|
||||
self.filter_field[field_name] = {"data": f'{escaped_value}%', "type": self.type_vague}
|
||||
|
||||
def contains(self, field_name, value: str):
|
||||
"""
|
||||
添加包含条件。
|
||||
添加安全包含条件(自动转义特殊字符,等效于vague)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value: 包含的值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value: 需要包含的字符串
|
||||
"""
|
||||
if value and value != '':
|
||||
self.filter_field[field_name] = {"data": value, "type": self.type_contains}
|
||||
escaped_value = self.escape_like(value)
|
||||
self.filter_field[field_name] = {"data": f'%{escaped_value}%', "type": self.type_contains}
|
||||
|
||||
def between(self, field_name, value1, value2):
|
||||
"""
|
||||
添加范围查询条件。
|
||||
添加范围查询条件(自动过滤无效值)
|
||||
|
||||
:param field_name: 模型字段名称。
|
||||
:param value1: 范围起始值。
|
||||
:param value2: 范围结束值。
|
||||
:param field_name: 模型字段名称
|
||||
:param value1: 范围起始值
|
||||
:param value2: 范围结束值
|
||||
"""
|
||||
if value1 and value2 and value1 != '' and value2 != '':
|
||||
if all([v is not None and v != '' for v in [value1, value2]]):
|
||||
self.filter_field[field_name] = {"data": [value1, value2], "type": self.type_between}
|
||||
|
||||
def get_filter(self, model: db.Model):
|
||||
"""
|
||||
获取最终的 SQLAlchemy 过滤条件。
|
||||
生成安全的SQLAlchemy过滤条件
|
||||
|
||||
:param model: SQLAlchemy 模型类。
|
||||
:return: 返回组合后的过滤条件。
|
||||
:param model: SQLAlchemy 模型类
|
||||
:return: 组合后的过滤条件(使用and_连接)
|
||||
"""
|
||||
for k, v in self.filter_field.items():
|
||||
if v.get("type") == self.type_vague:
|
||||
self.filter_list.append(getattr(model, k).like(v.get("data")))
|
||||
if v.get("type") == self.type_contains:
|
||||
self.filter_list.append(getattr(model, k).contains(v.get("data")))
|
||||
if v.get("type") == self.type_exact:
|
||||
self.filter_list.append(getattr(model, k) == v.get("data"))
|
||||
if v.get("type") == self.type_neq:
|
||||
self.filter_list.append(getattr(model, k) != v.get("data"))
|
||||
if v.get("type") == self.type_greater:
|
||||
self.filter_list.append(getattr(model, k) > v.get("data"))
|
||||
if v.get("type") == self.type_less:
|
||||
self.filter_list.append(getattr(model, k) < v.get("data"))
|
||||
if v.get("type") == self.type_between:
|
||||
self.filter_list.append(getattr(model, k).between(v.get("data")[0], v.get("data")[1]))
|
||||
field = getattr(model, k)
|
||||
data = v.get("data")
|
||||
query_type = v.get("type")
|
||||
|
||||
if query_type == self.type_vague:
|
||||
self.filter_list.append(field.like(data, escape='\\'))
|
||||
elif query_type == self.type_contains:
|
||||
self.filter_list.append(field.like(data, escape='\\'))
|
||||
elif query_type == self.type_exact:
|
||||
self.filter_list.append(field == data)
|
||||
elif query_type == self.type_neq:
|
||||
self.filter_list.append(field != data)
|
||||
elif query_type == self.type_greater:
|
||||
self.filter_list.append(field > data)
|
||||
elif query_type == self.type_less:
|
||||
self.filter_list.append(field < data)
|
||||
elif query_type == self.type_between:
|
||||
self.filter_list.append(field.between(data[0], data[1]))
|
||||
|
||||
return and_(*self.filter_list)
|
||||
@@ -65,29 +65,47 @@ class Query(BaseQuery):
|
||||
page = request.args.get('page', type=int)
|
||||
if limit is None:
|
||||
limit = request.args.get('limit', type=int)
|
||||
|
||||
return self.paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
error_out=False
|
||||
)
|
||||
|
||||
def layui_paginate_json(self, schema, page=None, limit=None):
|
||||
if page is None:
|
||||
page = request.args.get('page', 1, type=int) # 添加默认值
|
||||
if limit is None:
|
||||
limit = request.args.get('limit', 10, type=int) # 添加默认值
|
||||
|
||||
def layui_paginate_json(self, schema: Marshmallow().Schema):
|
||||
"""
|
||||
返回dict
|
||||
"""
|
||||
_res = self.paginate(
|
||||
page=request.args.get('page', type=int),
|
||||
per_page=request.args.get('limit', type=int),
|
||||
page=page,
|
||||
per_page=limit,
|
||||
error_out=False
|
||||
)
|
||||
return schema(many=True).dump(_res.items), _res.total, _res.page, _res.per_page
|
||||
|
||||
def layui_paginate_db_json(self):
|
||||
"""
|
||||
db.query(A.name).layui_paginate_db_json()
|
||||
"""
|
||||
_res = self.paginate(page=request.args.get('page', type=int),
|
||||
per_page=request.args.get('limit', type=int),
|
||||
error_out=False)
|
||||
return [dict(i) for i in _res.items], _res.total
|
||||
def layui_paginate_db_json(self, page=None, limit=None):
|
||||
if page is None:
|
||||
page = request.args.get('page', 1, type=int) # 添加默认值
|
||||
if limit is None:
|
||||
limit = request.args.get('limit', 10, type=int) # 添加默认值
|
||||
|
||||
_res = self.paginate(
|
||||
page=page,
|
||||
per_page=limit,
|
||||
error_out=False
|
||||
)
|
||||
|
||||
# 获取查询的列名列表
|
||||
column_names = [col["name"] for col in self.column_descriptions]
|
||||
|
||||
# 将元组转换为字典(支持单列或多列)
|
||||
data = [
|
||||
dict(zip(column_names, row))
|
||||
for row in _res.items
|
||||
]
|
||||
|
||||
return data, _res.total, _res.page, _res.per_page
|
||||
|
||||
|
||||
db = SQLAlchemy(query_class=Query)
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0" version="26.0.6">
|
||||
<diagram name="第 1 页" id="MiUs9oOc82b6Rctnqw0G">
|
||||
<mxGraphModel dx="2040" dy="734" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<mxGraphModel dx="1838" dy="612" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-76" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-1" target="oXrC6FassujB1rqgt1jG-2">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-76" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-1" target="oXrC6FassujB1rqgt1jG-2" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-1" value="<div><span style="background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));">app = create_app()</span></div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">app.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-1" value="<div><span style="background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));">app = create_app()</span></div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">app.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="-20" y="220" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-2" target="oXrC6FassujB1rqgt1jG-3">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-2" target="oXrC6FassujB1rqgt1jG-3" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-2" value="<div>初始化 Flask()</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-2" value="<div>初始化 Flask()</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="-20" y="130" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-3" target="oXrC6FassujB1rqgt1jG-4">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-3" target="oXrC6FassujB1rqgt1jG-4" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-23" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-3" target="oXrC6FassujB1rqgt1jG-22">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-23" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-3" target="oXrC6FassujB1rqgt1jG-22" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-3" value="<div>载入配置</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-3" value="<div>载入配置</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="150" y="130" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-4" target="oXrC6FassujB1rqgt1jG-5">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-4" target="oXrC6FassujB1rqgt1jG-5" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-26" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-4" target="oXrC6FassujB1rqgt1jG-25">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-26" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-4" target="oXrC6FassujB1rqgt1jG-25" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-4" value="<div>注册组件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-4" value="<div>注册组件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="130" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-5" target="oXrC6FassujB1rqgt1jG-6">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-5" target="oXrC6FassujB1rqgt1jG-6" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-50" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-5" target="oXrC6FassujB1rqgt1jG-49">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-50" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-5" target="oXrC6FassujB1rqgt1jG-49" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-5" value="<div>注册应用蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-5" value="<div>注册应用蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="516" y="130" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-67" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-6" target="oXrC6FassujB1rqgt1jG-66">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-67" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-6" target="oXrC6FassujB1rqgt1jG-66" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-6" target="oXrC6FassujB1rqgt1jG-71">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="oXrC6FassujB1rqgt1jG-6" target="oXrC6FassujB1rqgt1jG-71" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="910" y="351.33331298828125" as="targetPoint" />
|
||||
<Array as="points">
|
||||
@@ -55,192 +55,198 @@
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-6" value="<div>注册命令</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-6" value="<div>注册命令</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="700" y="130" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-22" value="<div>读取 BaseConfig</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">配置来自&nbsp;applications/config.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-22" value="<div>读取 BaseConfig</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">配置来自&nbsp;applications/config.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="140" y="190" width="140" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-28" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-25" target="oXrC6FassujB1rqgt1jG-27">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-28" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-25" target="oXrC6FassujB1rqgt1jG-27" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-25" value="<div>初始化插件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_plugins.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-25" value="<div>初始化插件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_plugins.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="190" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-30" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-27" target="oXrC6FassujB1rqgt1jG-29">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-30" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-27" target="oXrC6FassujB1rqgt1jG-29" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-27" value="<div>广播插件&nbsp;event_begin 事件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_plugins.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-27" value="<div>广播插件&nbsp;event_begin 事件</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_plugins.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="250" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-32" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-29" target="oXrC6FassujB1rqgt1jG-31">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-32" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-29" target="oXrC6FassujB1rqgt1jG-31" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-29" value="<div>初始化 Flask LoginManager</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_login.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-29" value="<div>初始化 Flask LoginManager</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/extensions/init_login.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="310" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-42" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-31" target="oXrC6FassujB1rqgt1jG-37">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-42" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-31" target="oXrC6FassujB1rqgt1jG-37" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-31" value="<div>初始化 SQLAlchemy 数据库</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_sqlalchemy.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-31" value="<div>初始化 SQLAlchemy 数据库</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_sqlalchemy.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="370" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-44" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-37" target="oXrC6FassujB1rqgt1jG-43">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-44" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-37" target="oXrC6FassujB1rqgt1jG-43" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-37" value="<div>初始化 Mail 邮件组件</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_error_views.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-37" value="<div>初始化 Mail 邮件组件</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_error_views.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="430" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-39" value="<div>初始化 服务器错误蓝图</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_error_views.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-39" value="<div>初始化 服务器错误蓝图</div><div><font style="color: rgb(77, 77, 77); font-size: 8px;">applications/extensions/init_error_views.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="670" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-48" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-41" target="oXrC6FassujB1rqgt1jG-39">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-48" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-41" target="oXrC6FassujB1rqgt1jG-39" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-41" value="<div>初始化 网页模板公用函数</div><div><font style="color: rgb(77, 77, 77); font-size: 7px;">applications/extensions/init_template_directives.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-41" value="<div>初始化 网页模板公用函数</div><div><font style="color: rgb(77, 77, 77); font-size: 7px;">applications/extensions/init_template_directives.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="610" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-46" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-43" target="oXrC6FassujB1rqgt1jG-45">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-46" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-43" target="oXrC6FassujB1rqgt1jG-45" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-43" value="<div>初始化<font style="font-size: 9px;"> Migrate </font>数据库迁移组件</div><div><font style="color: rgb(77, 77, 77); font-size: 9px;">applications/extensions/init_migrate.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="1tN3H2LKsTGCSpzGhpWD-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-43" target="1tN3H2LKsTGCSpzGhpWD-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-43" value="<div>初始化<font style="font-size: 9px;"> Migrate </font>数据库迁移组件</div><div><font style="color: rgb(77, 77, 77); font-size: 9px;">applications/extensions/init_migrate.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="490" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-47" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-45" target="oXrC6FassujB1rqgt1jG-41">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-47" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-45" target="oXrC6FassujB1rqgt1jG-41" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-45" value="<div>初始化<font size="1">&nbsp;Session 会话</font>组件</div><div><font style="color: rgb(77, 77, 77); font-size: 9px;">applications/extensions/init_session.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-45" value="<div>初始化<font size="1">&nbsp;Session 会话</font>组件</div><div><font style="color: rgb(77, 77, 77); font-size: 9px;">applications/extensions/init_session.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="310" y="550" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-54" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-49" target="oXrC6FassujB1rqgt1jG-52">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-54" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-49" target="oXrC6FassujB1rqgt1jG-52" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-49" value="<div>注册应用子页面路由</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-49" value="<div>注册应用子页面路由</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="190" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-63" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-51" target="oXrC6FassujB1rqgt1jG-62">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-63" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-51" target="oXrC6FassujB1rqgt1jG-62" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-51" value="<div>广播插件&nbsp;event_init 事件</div><div><span style="color: rgb(77, 77, 77); font-size: 9px;">applications/view/__init__.py</span></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-51" value="<div>广播插件&nbsp;event_init 事件</div><div><span style="color: rgb(77, 77, 77); font-size: 9px;">applications/view/__init__.py</span></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="610" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-56" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-52" target="oXrC6FassujB1rqgt1jG-55">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-56" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-52" target="oXrC6FassujB1rqgt1jG-55" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-52" value="<div>注册 用户管理 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-52" value="<div>注册 用户管理 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="250" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-58" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-55" target="oXrC6FassujB1rqgt1jG-57">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-58" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-55" target="oXrC6FassujB1rqgt1jG-57" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-55" value="<div>注册 文件上传 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-55" value="<div>注册 文件上传 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="310" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-60" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=none;endFill=0;dashPattern=1 4;strokeWidth=4;" edge="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-60" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=none;endFill=0;dashPattern=1 4;strokeWidth=4;" parent="1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="575.66" y="428" as="sourcePoint" />
|
||||
<mxPoint x="575.66" y="478" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-57" value="<div>注册 系统监控 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-57" value="<div>注册 系统监控 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="370" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-61" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-59" target="oXrC6FassujB1rqgt1jG-51">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-61" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-59" target="oXrC6FassujB1rqgt1jG-51" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-59" value="<div>注册 后台首页 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-59" value="<div>注册 后台首页 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="550" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-62" value="<div>注册 启用插件所提供的 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">plugins/*/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-62" value="<div>注册 启用插件所提供的 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">plugins/*/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="670" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-65" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-64" target="oXrC6FassujB1rqgt1jG-59">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-65" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-64" target="oXrC6FassujB1rqgt1jG-59" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-64" value="<div>注册 部门管理 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-64" value="<div>注册 部门管理 蓝图</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/system/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="496" y="490" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-69" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-66" target="oXrC6FassujB1rqgt1jG-68">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-69" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-66" target="oXrC6FassujB1rqgt1jG-68" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-66" value="<div>注册 应用 cli 命令</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-66" value="<div>注册 应用 cli 命令</div><div><font style="font-size: 9px; color: rgb(77, 77, 77);">applications/view/__init__.py</font></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="680" y="190" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-68" value="<div>广播插件&nbsp;event_finish 事件</div><div><span style="color: rgb(77, 77, 77); font-size: 9px;">applications/view/__init__.py</span></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-68" value="<div>广播插件&nbsp;event_finish 事件</div><div><span style="color: rgb(77, 77, 77); font-size: 9px;">applications/view/__init__.py</span></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="680" y="250" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-71" target="oXrC6FassujB1rqgt1jG-80">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="oXrC6FassujB1rqgt1jG-71" target="oXrC6FassujB1rqgt1jG-80" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-71" value="<div>初始化完成</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-71" value="<div>初始化完成</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="700" y="310" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-75" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-72" target="oXrC6FassujB1rqgt1jG-1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-75" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="oXrC6FassujB1rqgt1jG-72" target="oXrC6FassujB1rqgt1jG-1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-72" value="<div>初始化开始</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-72" value="<div>初始化开始</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="-20" y="310" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-78" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;strokeColor=#007FFF;strokeWidth=2;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-78" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;strokeColor=#007FFF;strokeWidth=2;" parent="1" vertex="1">
|
||||
<mxGeometry x="490" y="245" width="172" height="355" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-79" value="<font style="color: rgb(0, 127, 255);">注册项目的视图函数,</font><div><font style="color: rgb(0, 127, 255);">比如用户管理、文件上传等</font></div><div><font style="color: rgb(0, 127, 255);">功能都是在这里初始化的</font></div>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-79" value="<font style="color: rgb(0, 127, 255);">注册项目的视图函数,</font><div><font style="color: rgb(0, 127, 255);">比如用户管理、文件上传等</font></div><div><font style="color: rgb(0, 127, 255);">功能都是在这里初始化的</font></div>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="720" y="550" width="160" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-80" value="<div>广播插件&nbsp;event_context 事件</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-80" value="<div>广播插件&nbsp;event_context 事件</div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;dashed=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="680" y="370" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-82" value="<font style="color: rgb(153, 153, 153);">等同于 with app.app_context()</font>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-82" value="<font style="color: rgb(153, 153, 153);">等同于 with app.app_context()</font>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="800" y="430" width="170" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-84" value="<font style="color: rgb(255, 128, 0);">flask admin init 数据库</font><div><font style="color: rgb(255, 128, 0);">初始化的<span style="background-color: transparent;">命令是在这里注册的</span></font></div>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeColor=none;strokeWidth=1;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-84" value="<font style="color: rgb(255, 128, 0);">flask admin init 数据库</font><div><font style="color: rgb(255, 128, 0);">初始化的<span style="background-color: transparent;">命令是在这里注册的</span></font></div>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;strokeColor=none;strokeWidth=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="880" y="230" width="170" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-86" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;strokeColor=#FF8000;" edge="1" parent="1" target="oXrC6FassujB1rqgt1jG-84">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-86" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;strokeColor=#FF8000;" parent="1" target="oXrC6FassujB1rqgt1jG-84" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="840" y="210" as="sourcePoint" />
|
||||
<mxPoint x="880" y="240" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-88" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#999999;" edge="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-88" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#999999;" parent="1" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="840" y="412.5" as="sourcePoint" />
|
||||
<mxPoint x="873" y="432.5" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-89" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#007FFF;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-78">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-89" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#007FFF;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="oXrC6FassujB1rqgt1jG-78" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="670" y="520" as="sourcePoint" />
|
||||
<mxPoint x="720" y="550" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-90" value="<font style="color: rgb(0, 153, 0);">开发插件的蓝图在这里注册</font>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-90" value="<font style="color: rgb(0, 153, 0);">开发插件的蓝图在这里注册</font>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="700" y="650" width="150" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-92" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#009900;" edge="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-92" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#009900;" parent="1" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="662" y="630" as="sourcePoint" />
|
||||
<mxPoint x="720" y="649" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-93" value="<font style="color: rgb(150, 150, 150);">默认读取的是</font><div><font style="color: rgb(150, 150, 150);">&nbsp;BaseConfig 类中的配置。</font><div><span style="background-color: transparent; color: light-dark(rgb(150, 150, 150), rgb(108, 108, 108));">修改其他类请先在文件</span></div><div><div><font style="color: rgb(150, 150, 150);"><span style="background-color: transparent;">applications/__init__.py&nbsp;</span></font></div><div><font style="color: rgb(150, 150, 150);"><span style="background-color: transparent;">中进行修改</span></font></div></div></div>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-93" value="<font style="color: rgb(150, 150, 150);">默认读取的是</font><div><font style="color: rgb(150, 150, 150);">&nbsp;BaseConfig 类中的配置。</font><div><span style="background-color: transparent; color: light-dark(rgb(150, 150, 150), rgb(108, 108, 108));">修改其他类请先在文件</span></div><div><div><font style="color: rgb(150, 150, 150);"><span style="background-color: transparent;">applications/__init__.py&nbsp;</span></font></div><div><font style="color: rgb(150, 150, 150);"><span style="background-color: transparent;">中进行修改</span></font></div></div></div>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="125" y="260" width="145" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-95" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#999999;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-22" target="oXrC6FassujB1rqgt1jG-93">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-95" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#999999;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="oXrC6FassujB1rqgt1jG-22" target="oXrC6FassujB1rqgt1jG-93" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="200" y="240" as="sourcePoint" />
|
||||
<mxPoint x="233" y="260" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-96" value="<font style="color: rgb(204, 0, 0);">404 500 等请求、服务器错误的视图会在这里注册</font>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-96" value="<font style="color: rgb(204, 0, 0);">404 500 等请求、服务器错误的视图会在这里注册</font>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="130" y="620" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-97" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#CC0000;exitX=0.709;exitY=0.962;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitPerimeter=0;" edge="1" parent="1" source="oXrC6FassujB1rqgt1jG-96" target="oXrC6FassujB1rqgt1jG-39">
|
||||
<mxCell id="oXrC6FassujB1rqgt1jG-97" value="" style="endArrow=none;html=1;rounded=0;dashed=1;endFill=0;strokeColor=#CC0000;exitX=0.709;exitY=0.962;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="oXrC6FassujB1rqgt1jG-96" target="oXrC6FassujB1rqgt1jG-39" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="240" y="670" as="sourcePoint" />
|
||||
<mxPoint x="298" y="709" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="1tN3H2LKsTGCSpzGhpWD-3" value="导入所有数据库模型<div><span style="color: rgb(77, 77, 77); font-size: 9px;">applications/models/__init__.py</span></div>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxGeometry x="130" y="490" width="160" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 827 KiB After Width: | Height: | Size: 835 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -26,3 +26,7 @@ language = 'zh_CN'
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
|
||||
suppress_warnings = [
|
||||
'misc.highlighting_failure' # 忽略代码块高亮失败警告
|
||||
]
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _简单增删改查模块:
|
||||
|
||||
:mod:`curd` -- 简单增删改查模块
|
||||
=======================================
|
||||
|
||||
|
||||
@@ -39,6 +39,14 @@
|
||||
|
||||
初始化过滤条件存储字典和列表。
|
||||
|
||||
.. method:: escape_like(value: str, escape_char: str = '\\')
|
||||
|
||||
转义LIKE查询中的特殊字符(%, _ 和转义字符本身)
|
||||
|
||||
:param value: 需要转义的原始字符串
|
||||
:param escape_char: 转义字符(默认反斜杠)
|
||||
:return: 转义后的安全字符串
|
||||
|
||||
|
||||
.. method:: exact(field_name, value)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
目录索引
|
||||
|
||||
公共函数
|
||||
公共模块
|
||||
------------
|
||||
|
||||
.. toctree::
|
||||
@@ -12,7 +12,7 @@
|
||||
curd
|
||||
helper
|
||||
|
||||
辅助函数
|
||||
辅助模块
|
||||
------------
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. title:: 辅助函数
|
||||
.. title:: 辅助模块
|
||||
|
||||
目录索引
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
.. _后端页面编写:
|
||||
|
||||
后端页面编写
|
||||
======================
|
||||
|
||||
该章节将介绍如何在 Pear Admin Flask 中编写一个新后端页面,此章节将会以编写积分兑奖系统为例,编写一个兑换码管理的后台页面。
|
||||
该章节将介绍如何在 Pear Admin Flask 中编写一个新后端页面,此章节将会以编写兑换码管理页面为例,编写一个兑换码管理的后台页面。
|
||||
|
||||
.. note::
|
||||
|
||||
前端页面制作,请参考 :ref:`简单前端页面示例` 章节。
|
||||
|
||||
.. _项目初始化逻辑:
|
||||
|
||||
@@ -22,6 +28,468 @@
|
||||
由此可以看出,我们想要添加自己的后端页面,可以在 “注册项目的视图函数”(蓝框) 的地方添加,当然,同样页面可以作为插件的方式接入以提高项目的拓展性。
|
||||
下面将介绍如何在这两种方式下添加自己的后台页面。
|
||||
|
||||
设计数据库
|
||||
-----------------------
|
||||
|
||||
兑换码一定是保存在数据库中的,我们现在要求改程序至少有以下几个功能:
|
||||
|
||||
* 可以通过 flask admin init 或者等价的命令初始化数据库
|
||||
* 数据库存在统一管理的模型
|
||||
* 数据库的内容方便数据转化
|
||||
|
||||
数据的字段可以定为:
|
||||
|
||||
* id -- 唯一主键
|
||||
* key -- 兑换码
|
||||
* content -- 具体的内容
|
||||
* enable -- 是否启用
|
||||
* used -- 是否使用
|
||||
* create_at -- 创建时间
|
||||
|
||||
根据上述需求,我们可以设计出这样一个数据库 Model :
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import datetime
|
||||
from applications.extensions import db
|
||||
|
||||
|
||||
class Gift(db.Model):
|
||||
__tablename__ = 'admin_gift'
|
||||
id = db.Column(db.Integer, primary_key=True, comment="唯一ID")
|
||||
key = db.Column(db.String(50), comment="兑换码")
|
||||
content = db.Column(db.String(), comment="具体的内容")
|
||||
enable = db.Column(db.Integer, default=0, comment='是否启用')
|
||||
used = db.Column(db.Integer, default=0, comment='是否已经使用')
|
||||
create_at = db.Column(db.DateTime, default=datetime.datetime.now, comment='创建时间')
|
||||
|
||||
我们将该文件命名为 `admin_gift.py` 放置在 `applications/models/admin_gift.py` ,而后为了使程序可以调用到这个模型,
|
||||
需要在 `applications/models/__init__.py` 中导入这个模型。
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from .admin_gift import Gift
|
||||
|
||||
.. note::
|
||||
|
||||
由于 Gift 模型是 db.Model 的子类,在使用 `flask db init` 等命令行初始化数据库时,自动对 Gift 表进行创建,前提是这个类已经被 Python 加载,
|
||||
换而言之,Python 会自动将 db.Model 的子类作为数据库的一部分,加入到数据库初始化中。
|
||||
|
||||
|
||||
初始化数据库
|
||||
-----------------------
|
||||
|
||||
通过上面的操作,我们已经成功将数据库中的 admin_gift 表进行创建。现在我们希望在使用 `flask admin init` 的时候,可以将我们已经定义的数据写入到数据表中。
|
||||
|
||||
在 `applications/common/script` 目录中,撰写了默认数据的写入脚本(也就是 Flask 启动最后加载的项目),
|
||||
我们要做的是在 `applications/common/script/admin.py` 中添加自己需要的数据,我们可以对其进行修改,添加如下的代码:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
from applications.models import Gift
|
||||
|
||||
...
|
||||
now_time = datetime.datetime.now()
|
||||
...
|
||||
powerdata = [
|
||||
...
|
||||
Power(
|
||||
id=60,
|
||||
name='兑换码管理',
|
||||
type='1',
|
||||
code='system:gift:main',
|
||||
url='/system/gift/',
|
||||
open_type='_iframe',
|
||||
parent_id='1',
|
||||
icon='layui-icon layui-icon layui-icon layui-icon-diamond',
|
||||
sort=8,
|
||||
create_time=now_time,
|
||||
enable=1
|
||||
), Power(
|
||||
id=61,
|
||||
name='兑换码添加',
|
||||
type='2',
|
||||
code='system:gift:add',
|
||||
url='',
|
||||
open_type='',
|
||||
parent_id='60',
|
||||
icon='',
|
||||
sort=0,
|
||||
create_time=now_time,
|
||||
enable=1
|
||||
), Power(
|
||||
id=62,
|
||||
name='兑换码删除',
|
||||
type='2',
|
||||
code='system:gift:remove',
|
||||
url='',
|
||||
open_type='',
|
||||
parent_id='60',
|
||||
icon='',
|
||||
sort=0,
|
||||
create_time=now_time,
|
||||
enable=1
|
||||
), Power(
|
||||
id=63,
|
||||
name='兑换码编辑',
|
||||
type='2',
|
||||
code='system:gift:edit',
|
||||
url='',
|
||||
open_type='',
|
||||
parent_id='60',
|
||||
icon='',
|
||||
sort=0,
|
||||
create_time=now_time,
|
||||
enable=1
|
||||
),
|
||||
...
|
||||
]
|
||||
giftdata = [
|
||||
Gift(
|
||||
id=0,
|
||||
key='myTestCode',
|
||||
content='8折优惠',
|
||||
enable=1,
|
||||
used=0,
|
||||
create_at=now_time
|
||||
),
|
||||
Gift(
|
||||
id=1,
|
||||
key='DisableCode',
|
||||
content='1折优惠',
|
||||
enable=0,
|
||||
used=0,
|
||||
create_at=now_time
|
||||
)
|
||||
]
|
||||
|
||||
...
|
||||
def add_role_power():
|
||||
admin_powers = Power.query.filter(Power.id.in_([1, 3, 4, 9, 12, 13, 17, 18, 44, 48, 60])).all()
|
||||
...
|
||||
|
||||
@admin_cli.command("init")
|
||||
def init_db():
|
||||
...
|
||||
db.session.add_all(giftdata)
|
||||
...
|
||||
|
||||
这样就可以将数据库内容写入了。
|
||||
|
||||
.. note::
|
||||
|
||||
powerdata 中添加了对兑换码的操作权限,可以先去“权限管理”中添加,而后再从数据中抄取。
|
||||
或者忽略初始化时对 powerdata 的添加,在初始化之后,手动在权限管理中添加。
|
||||
|
||||
使用 Schema 序列化
|
||||
---------------------------
|
||||
|
||||
与前端交互大多用的是 JSON 格式的数据,这就涉及到将数据库查询的结果对象(Query)转化为 JSON 这一步骤。
|
||||
我们可以使用 flask_marshmallow 中的 SQLAlchemyAutoSchema 将数据库查询对象转换为 JSON 格式。
|
||||
|
||||
创建文件 `applications/schemas/admin_gift.py` ,并继承 SQLAlchemyAutoSchema ,更改其中的目标模型为我们创建的 Gift 模型。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask_marshmallow.sqla import SQLAlchemyAutoSchema
|
||||
from applications.models import Gift
|
||||
|
||||
|
||||
class GiftSchema(SQLAlchemyAutoSchema):
|
||||
class Meta:
|
||||
model = Gift # table = models.Album.__table__
|
||||
include_fk = True # 序列化阶段是否也一并返回主键
|
||||
|
||||
.. note::
|
||||
|
||||
更多序列化参数可以参考 :ref:`Schema 序列化` 章节。
|
||||
|
||||
随后,在 `applications/schemas/__init__.py` 引用,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
from .admin_gift import GiftSchema
|
||||
|
||||
这一步不是必须的,但是应通过这一步的引用,可以在蓝图页面中,方便的使用 `from applications.schemas import *` 的方式导入。
|
||||
|
||||
编写后端视图函数
|
||||
-----------------------
|
||||
|
||||
注册蓝图
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
接着我们需要设计后端的数据增删改查部分的视图函数,创建文件 `applications/view/system/gift.py` ,并写上基本的蓝图初始化逻辑:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint('gift', __name__, url_prefix='/gift')
|
||||
|
||||
根据流程图,我们需要在 `applications/view/system/__init__.py` 中,注册 `gift.py` 的蓝图:
|
||||
|
||||
.. code-block::
|
||||
|
||||
...
|
||||
from applications.view.system.gift import bp as gift_bp
|
||||
...
|
||||
|
||||
def register_system_bps(app: Flask):
|
||||
...
|
||||
system_bp.register_blueprint(gift_bp)
|
||||
...
|
||||
|
||||
|
||||
.. _编写数据获取路由:
|
||||
|
||||
编写数据获取路由
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. important::
|
||||
|
||||
因为目标是让前端 layui 的动态表格获取数据,而根据 layui 的文档,表格将会提供 limit 和 page 两个查询参数来进行分页查询,所以要对 limit 和 page 进行处理。
|
||||
|
||||
现在开始编写数据获取路由,路由是以 JSON 格式响应数据库中 `admin_gift` 的数据,下面提供一种实现方法:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
from applications.models import Gift
|
||||
from applications.schemas import GiftSchema
|
||||
from applications.extensions import db
|
||||
|
||||
from applications.common.utils.http import table_api
|
||||
from applications.common.utils.rights import authorize
|
||||
|
||||
bp = Blueprint('gift', __name__, url_prefix='/gift')
|
||||
|
||||
|
||||
@bp.get('/data')
|
||||
@authorize("system:gift:main")
|
||||
def data():
|
||||
|
||||
query = db.session.query(Gift).layui_paginate()
|
||||
|
||||
return table_api(
|
||||
data=GiftSchema(many=True).dump(query),
|
||||
count=query.total,
|
||||
limit=query.per_page
|
||||
)
|
||||
|
||||
可以发现,在没有搜索的情况下,正确处理前端的分页查询,实际上只有简单 5 行代码就可以完成(自动处理了 limit 和 page 参数),
|
||||
另外,也可以采用已经封装好的 `layui_paginate_json` 方法:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
@bp.get('/data')
|
||||
@authorize("system:gift:main")
|
||||
def data():
|
||||
|
||||
data, total, page, limit = db.session.query(Gift).layui_paginate_json(GiftSchema)
|
||||
|
||||
return table_api(
|
||||
data=data,
|
||||
count=total,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
`layui_paginate_json` 函数完成了分页、解析与转化,适用于一些比较简单数据转化场景。
|
||||
|
||||
.. warning::
|
||||
|
||||
对于任何形式的后台管理员路由,切记不要忘记添加 `authorize` 装饰函数对请求效验权限!!!!!!
|
||||
|
||||
.. note::
|
||||
|
||||
对于 layui_paginate 方法定义,可以查看 :ref:`与 layui 的数据格式同步` 章节。
|
||||
|
||||
访问路由 `/system/gift/data` 可以获得如下数据:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"code": 0,
|
||||
"count": 2,
|
||||
"data": [
|
||||
{
|
||||
"content": "8折优惠",
|
||||
"create_at": "2025-01-28T19:10:48.607165",
|
||||
"enable": 1,
|
||||
"id": 0,
|
||||
"key": "myTestCode",
|
||||
"used": 0
|
||||
},
|
||||
{
|
||||
"content": "1折优惠",
|
||||
"create_at": "2025-01-28T19:10:48.607165",
|
||||
"enable": 0,
|
||||
"id": 1,
|
||||
"key": "DisableCode",
|
||||
"used": 0
|
||||
}
|
||||
],
|
||||
"limit": 10,
|
||||
"msg": ""
|
||||
}
|
||||
|
||||
随后,我们加入查询:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
@bp.get('/data')
|
||||
@authorize("system:gift:main")
|
||||
def data():
|
||||
key = request.args.get('key', type=str)
|
||||
|
||||
mf = ModelFilter()
|
||||
if key:
|
||||
mf.vague('key', key) # 模糊查询
|
||||
|
||||
data, total, page, limit = db.session.query(Gift).filter(mf.get_filter(Gift)).layui_paginate_json(GiftSchema)
|
||||
|
||||
return table_api(
|
||||
data=data,
|
||||
count=total,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
.. _编写启用与禁用视图函数:
|
||||
|
||||
编写启用与禁用视图函数
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
启用与禁用的基本思路是通过 ID 筛选到合适的记录行并设置其 enable 为 1 或者 0。在设计数据库时,我们有意将表示启用禁用字段设置为 `enable` 以此可以使用项目已经封装好的函数。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from applications.common.curd import enable_status, disable_status
|
||||
|
||||
@bp.put('/enable')
|
||||
@authorize("system:gift:edit")
|
||||
def enable_api():
|
||||
data = request.get_json(force=True)
|
||||
|
||||
if enable_status(Gift, data.get('id')):
|
||||
return success_api(msg="启用成功")
|
||||
|
||||
return success_api(msg="启用失败")
|
||||
|
||||
|
||||
@bp.put('/disable')
|
||||
@authorize("system:gift:edit")
|
||||
def disable_api():
|
||||
req_json = request.get_json(force=True)
|
||||
|
||||
if disable_status(Gift, req_json.get('id')):
|
||||
return success_api(msg="禁用成功")
|
||||
|
||||
return success_api(msg="禁用失败")
|
||||
|
||||
.. note::
|
||||
|
||||
对于上述的 `enable_status` `disable_status` 函数,可以参考文档 :ref:`简单增删改查模块` 章节。
|
||||
|
||||
数据的修改经历如下步骤:获取目标兑换码 ID、获取对应修改的新数据、应用修改,而数据的添加仅没有“获取目标兑换码 ID”这一步骤。
|
||||
|
||||
我们先来撰写添加这一部分的视图函数,
|
||||
|
||||
编写删除视图函数
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
删除的视图函数,实则和启用禁用是一样的,这里直接给出代码:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@bp.delete('/remove/<int:_id>')
|
||||
@authorize("system:gift:remove")
|
||||
def remove_api(_id):
|
||||
|
||||
if delete_one_by_id(Gift, _id):
|
||||
return success_api(msg="删除成功")
|
||||
|
||||
return success_api(msg="删除失败")
|
||||
|
||||
|
||||
你会注意到,由于 `curd` 模块的封装,使编写路由变的简洁。
|
||||
|
||||
.. _编写增加视图函数:
|
||||
|
||||
编写增加视图函数
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
添加视图函数经历下面几个步骤:获取参数、效验参数、写入数据库。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@bp.post('/save')
|
||||
@authorize("system:gift:add", log=True)
|
||||
def save():
|
||||
req_json = request.get_json(force=True)
|
||||
|
||||
data = {
|
||||
'key': req_json.get('key'),
|
||||
'content': req_json.get('content'),
|
||||
'enable': req_json.get('enable'),
|
||||
'used': 0
|
||||
}
|
||||
|
||||
# 效验参数
|
||||
if not all(list(data.keys())):
|
||||
return fail_api(msg="参数不全")
|
||||
|
||||
if not data['enable'].isdigit():
|
||||
return fail_api(msg="参数 enable 错误")
|
||||
|
||||
try:
|
||||
db.session.add(Gift(**data))
|
||||
db.session.commit()
|
||||
return success_api(msg="添加成功")
|
||||
except Exception as e:
|
||||
return fail_api(msg="添加失败")
|
||||
|
||||
.. important::
|
||||
|
||||
效验参数是必不可少的,要尽可能一切不相信用户的输入。
|
||||
|
||||
.. _编写修改视图函数:
|
||||
|
||||
编写修改视图函数
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
修改视图函数的编写就照葫芦画瓢即可,代码如下:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@bp.post('/update')
|
||||
@authorize("system:gift:edit", log=True)
|
||||
def update():
|
||||
req_json = request.get_json(force=True)
|
||||
|
||||
_id = req_json.get('id')
|
||||
|
||||
data = {
|
||||
'key': req_json.get('key'),
|
||||
'content': req_json.get('content'),
|
||||
'enable': req_json.get('enable'),
|
||||
'used': 0
|
||||
}
|
||||
|
||||
# 效验参数
|
||||
if not all(list(data.keys())):
|
||||
return fail_api(msg="参数不全")
|
||||
|
||||
if not data['enable'].isdigit():
|
||||
return fail_api(msg="参数 enable 错误")
|
||||
|
||||
try:
|
||||
db.session.query(Gift).filter(Gift.id == _id).update(data)
|
||||
db.session.commit()
|
||||
return success_api(msg="编辑成功")
|
||||
except Exception as e:
|
||||
return fail_api(msg="编辑失败")
|
||||
|
||||
@@ -77,3 +77,481 @@ Pear Admin Layui 的控制主题色逻辑是通过设置全局的 css 属性:`
|
||||
border-color: #4C4D4F;
|
||||
}
|
||||
|
||||
|
||||
.. _简单前端页面示例:
|
||||
|
||||
简单前端页面示例
|
||||
-------------------------
|
||||
|
||||
此部分我们来以制作一个兑换码管理的前端页面为例。
|
||||
|
||||
.. note::
|
||||
|
||||
配套后端的制作可以查看 :ref:`后端页面编写` 章节。
|
||||
|
||||
规划模板存放位置
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
模板一般存放在 `templates/system` 的目录下,该目录下的每一个子文件(夹)都是一个特定功能的实现的网页模板。
|
||||
|
||||
我们在其中创建一个 `gift` 文件夹,并放入 `main.html` 、`add.html` 和 `edit.html` 。
|
||||
|
||||
|
|
||||
|
||||
.. image:: ../_static/规划模板存放位置.png
|
||||
:align: center
|
||||
|
||||
|
|
||||
|
||||
加入动态表格与查询表单
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
随后,我们可以制作一个写一个简单的页面,设想是页面中存在一个查询表单和一个动态表格:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>兑换码管理</title>
|
||||
{% include 'system/common/header.html' %}
|
||||
</head>
|
||||
<body class="pear-container">
|
||||
|
||||
{# 查询表单 #}
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" action="" lay-filter="query-form">
|
||||
<div class="layui-form-item" style="margin-bottom: unset;">
|
||||
<label class="layui-form-label">激活码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="key" placeholder="" class="layui-input">
|
||||
</div>
|
||||
<button class="layui-btn layui-btn-md" lay-submit lay-filter="gift-query">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
查询
|
||||
</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary layui-btn-md">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# 用户表格 #}
|
||||
<div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<table id="gift-table" lay-filter="gift-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<!-- 这里写的都是表格一行元素组件 -->
|
||||
{% raw %}
|
||||
<script type="text/html" id="enable-element">
|
||||
<input type="checkbox" name="enable" value="{{ d.id }}" lay-skin="switch" lay-text="启用|禁用"
|
||||
lay-filter="gift-enable"
|
||||
{{# if(d.enable==1){ }} checked {{# } }}/>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="used-element">
|
||||
{{# if(d.used==1){ }}
|
||||
<span style="color: red">已用</span>
|
||||
{{# } else { }}
|
||||
<span style="color: green">未用</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="createTime-element">
|
||||
{{layui.util.toDateString(d.create_at, "yyyy-MM-dd HH:mm:ss")}}
|
||||
</script>
|
||||
{% endraw %}
|
||||
|
||||
|
||||
<script type="text/html" id="table-bar">
|
||||
{% if authorize("system:gift:edit") %}
|
||||
<button class="layui-btn layui-btn-xs" lay-event="edit"><i class="pear-icon pear-icon-edit"> 编辑</i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if authorize("system:gift:remove") %}
|
||||
<button class="layui-btn layui-btn-danger layui-btn-xs" lay-event="remove"><i
|
||||
class="pear-icon pear-icon-ashbin"> 删除</i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<!-- 这里是表格的工具栏 -->
|
||||
<script type="text/html" id="table-toolbar">
|
||||
{% if authorize("system:gift:add") %}
|
||||
<button class="layui-btn layui-btn-primary layui-btn-sm" lay-event="add">
|
||||
<i class="pear-icon pear-icon-add"></i>
|
||||
新增
|
||||
</button>
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
|
||||
{% include 'system/common/footer.html' %}
|
||||
<script>
|
||||
layui.use(['table'], function () {
|
||||
let table = layui.table;
|
||||
|
||||
// 表格数据
|
||||
let cols = [
|
||||
[
|
||||
{title: '编号', field: 'id', align: 'center'},
|
||||
{title: '激活码', field: 'key', align: 'center'},
|
||||
{title: '内容', field: 'content', align: 'center'},
|
||||
{title: '启用', field: 'enable', align: 'center', templet: '#enable-element'},
|
||||
{title: '已用', field: 'used', align: 'center', templet: '#used-element'},
|
||||
{title: '创建时间', field: 'create_at', templet: '#createTime-element', align: 'center'},
|
||||
{title: '操作', toolbar: '#table-bar', align: 'center', width: 180}
|
||||
]
|
||||
]
|
||||
|
||||
// 渲染表格数据
|
||||
table.render({
|
||||
elem: '#gift-table',
|
||||
url: '/system/gift/data', // 请求链接
|
||||
page: true,
|
||||
cols: cols,
|
||||
skin: 'line',
|
||||
toolbar: '#table-toolbar',
|
||||
text: {none: '暂无激活码信息'},
|
||||
defaultToolbar: [{layEvent: 'refresh', icon: 'layui-icon-refresh'}, 'filter', 'print', 'exports']
|
||||
})
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
注意还要在 Python 中加上渲染路由:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@bp.get('/')
|
||||
@authorize("system:gift:main")
|
||||
def index():
|
||||
return render_template('system/gift/main.html')
|
||||
|
||||
前端的效果如下:
|
||||
|
||||
|
|
||||
|
||||
.. image:: ../_static/兑换码管理页面.png
|
||||
:align: center
|
||||
|
||||
|
|
||||
|
||||
完善查询功能
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
查询数据库的视图函数可以参考 :ref:`编写数据获取路由` 章节。
|
||||
|
||||
接着,我们完善查询功能,确保获取的路由在有查询功能之后,我们在前端编写表单提交的处理。
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
layui.use(['table', 'form'], function () {
|
||||
...
|
||||
let form = layui.form;
|
||||
|
||||
...
|
||||
// 表单查询
|
||||
form.on('submit(gift-query)', function (data) {
|
||||
table.reload('gift-table', {where: data.field})
|
||||
return false;
|
||||
})
|
||||
}
|
||||
|
||||
监听启用和禁用事件
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
在动态表格中存在启用与禁用的切换开关,我们需要对开关进行监听,在用户切换开关状态时,自动在数据库中设置兑换码的启用与禁用状态。
|
||||
|
||||
.. note::
|
||||
|
||||
后台视图函数,参考 :ref:`编写启用与禁用视图函数` 章节。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
let $ = layui.jquery;
|
||||
ley popup = layui.popup;
|
||||
...
|
||||
|
||||
// 启用与禁用
|
||||
form.on('switch(gift-enable)', function (obj) {
|
||||
let operate;
|
||||
if (obj.elem.checked) {
|
||||
operate = 'enable'
|
||||
} else {
|
||||
operate = 'disable'
|
||||
}
|
||||
let loading = layer.load()
|
||||
$.ajax({
|
||||
url: '/system/gift/' + operate,
|
||||
data: JSON.stringify({id: this.value}),
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'put',
|
||||
success: function (result) {
|
||||
layer.close(loading)
|
||||
if (result.success) {
|
||||
popup.success(result.msg)
|
||||
} else {
|
||||
popup.failure(result.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
.. important::
|
||||
|
||||
如果对前端编写存在问题,可以自行查阅 `layui 官方文档 <https://layui.dev/>`_ ,需要注意的是,由于页面中的组件元素增多,
|
||||
最好使用准确无误的表示区分这些表单组件,以便在监听时正确绑定到事件。
|
||||
|
||||
监听删除数据事件
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
删除数据事件就与监听启用禁用其实是同理的,这里直接给出代码,
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// 表格各行工具事件
|
||||
table.on('tool(gift-table)', function (obj) {
|
||||
if (obj.event === 'remove') {
|
||||
|
||||
layer.confirm('确定要删除该兑换码?', {icon: 3, title: '提示'}, function (index) {
|
||||
layer.close(index)
|
||||
let loading = layer.load()
|
||||
$.ajax({
|
||||
url: '/system/gift/remove/' + obj.data['id'],
|
||||
dataType: 'json',
|
||||
type: 'delete',
|
||||
success: function (result) {
|
||||
layer.close(loading)
|
||||
if (result.success) {
|
||||
popup.success(result.msg, function () {
|
||||
obj.del()
|
||||
})
|
||||
} else {
|
||||
popup.failure(result.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
} else if (obj.event === 'edit') {
|
||||
// 待定
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
编写新建与编辑页面
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
对应的视图函数,可以查看 :ref:`编写增加视图函数` 章节。
|
||||
|
||||
编写这一部分涉及到设计表单,编辑实际上就是已经填好值的新建页面。由于目前项目暂未进行前后端分离,所以为了方便直接使用模板渲染的方式,直接将内容渲染到编辑页面上。
|
||||
这就导致需要保留这两个略微有差别的页面,后续的更新,将会尝试将渲染的方式剥离项目,直接动态请求,可以实现动态分离。
|
||||
|
||||
此处给出表单页面基本的写法,所有的新建页面表单可以参考这个模板:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>激活码管理</title>
|
||||
{% include 'system/common/header.html' %}
|
||||
</head>
|
||||
<body>
|
||||
<form class="layui-form">
|
||||
<div class="mainBox">
|
||||
<div class="main-container">
|
||||
<div class="main-container">
|
||||
<!-- 这里填写表单元素 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="button-container">
|
||||
<button type="submit" class="layui-btn layui-btn-sm" lay-submit="" lay-filter="save">
|
||||
<i class="layui-icon layui-icon-ok"></i>
|
||||
提交
|
||||
</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">
|
||||
<i class="layui-icon layui-icon-refresh"></i>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% include 'system/common/footer.html' %}
|
||||
<script>
|
||||
layui.use(['form', 'jquery'], function () {
|
||||
let form = layui.form
|
||||
let $ = layui.jquery
|
||||
|
||||
form.on('submit(save)', function (data) {
|
||||
|
||||
$.ajax({
|
||||
url: '目标保存页面',
|
||||
data: JSON.stringify(data.field),
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'post',
|
||||
success: function (result) {
|
||||
if (result.success) {
|
||||
layer.msg(result.msg, {icon: 1, time: 1000}, function () {
|
||||
parent.layer.close(parent.layer.getFrameIndex(window.name))//关闭当前页
|
||||
parent.layui.table.reload('gift-table') // 目标表格
|
||||
})
|
||||
} else {
|
||||
layer.msg(result.msg, {icon: 2, time: 1000})
|
||||
}
|
||||
}
|
||||
})
|
||||
return false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
撰写表单的工作较为简单,代码如下:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">兑换码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="key" lay-verify="title" autocomplete="off" placeholder="请输入兑换码"
|
||||
class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">兑换内容</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea placeholder="请输入兑换内容" name="content" class="layui-textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="enable" value="1" title="开启" checked>
|
||||
<input type="radio" name="enable" value="0" title="关闭">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
注意还要在管理页面加上窗口弹出的绑定:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// 顶部工具栏
|
||||
table.on('toolbar(gift-table)', function (obj) {
|
||||
if (obj.event === 'add') {
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '新增',
|
||||
shade: 0.1,
|
||||
area: ['550px', '550px'],
|
||||
content: '/system/gift/add'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
|
||||
|
||||
.. image:: ../_static/兑换码添加页面.png
|
||||
:align: center
|
||||
|
||||
|
|
||||
|
||||
|
||||
现在编写编辑页面,编辑页面相较于新建页面仅有两个区别:增加了 ID 编辑框、修改了提交的地址,最重要的是将后端传入的内容渲染到页面上。
|
||||
|
||||
我们先编写如下的路由视图:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@bp.get('/edit/<int:_id>')
|
||||
@authorize("system:gift:edit", log=True)
|
||||
def edit(_id):
|
||||
gift = get_one_by_id(Gift, _id)
|
||||
return render_template('system/gift/edit.html', gift=gift)
|
||||
|
||||
绑定编辑事件(就是在上面 “// 待定” 的地方添加内容):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
} else if (obj.event === 'edit') {
|
||||
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: '修改',
|
||||
shade: 0.1,
|
||||
area: ['550px', '500px'],
|
||||
content: '/system/gift/edit/' + obj.data['id']
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
设计新表单:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">编号</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="id" lay-verify="title" autocomplete="off" placeholder="请输入编号"
|
||||
class="layui-input" value="{{ gift.id }}" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">兑换码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="key" lay-verify="title" autocomplete="off" placeholder="请输入兑换码"
|
||||
class="layui-input" value="{{ gift.key }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">兑换内容</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea placeholder="请输入兑换内容" name="content" class="layui-textarea">{{ gift.content }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="enable" value="1" title="开启" {{ 'checked' if gift.enable == 1 }}>
|
||||
<input type="radio" name="enable" value="0" title="关闭" {{ 'checked' if gift.enable == 0 }}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
.. important::
|
||||
|
||||
要注意把内容渲染到网页上哦,其中 id 字段设置为禁用。随后不要忘记,将表单的提交地址改为 `/system/gift/update` 。
|
||||
|
||||
.. note::
|
||||
|
||||
对应的视图函数,可以查看 :ref:`编写修改视图函数` 章节。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
所有插件放置在 `plugins` 文件夹中,项目提供了三个示例插件,分别是 `helloworld` 、 `realip` 和 `replacePage` ,分别用于示例页面的注册、修改 Flask 上下文和页面替换。
|
||||
|
||||
像项目自带的用户管理、部门管理等基本功能属于程序自身的“功能插件”,对于大多数衍生项目来说,多的是修改字符串和删除部分不需要的功能,
|
||||
而插件开发主要可以用于添加自己的视图函数和功能,可以完美于项目融合,增加可拓展性。
|
||||
而插件开发主要可以用于添加自己的视图函数和功能,可以完美于项目融合,增加可拓展性,尤其是用于既想保留原项目功能又想填写新功能的项目开发。
|
||||
|
||||
插件的启用
|
||||
-----------------
|
||||
|
||||
@@ -40,11 +40,18 @@
|
||||
|
||||
在 Pear Admin Flask 中,菜单的管理归属于 “权限管理” ,这样做的原因是为了使不同用户可以使用不同的访问控制。所以需要修改菜单,需要在 “权限管理” 页面编辑即可。
|
||||
|
||||
配置后台站内消息
|
||||
-------------------
|
||||
|
||||
后台站内消息是异步获取的,其路由在 `applications/view/system/rights.py` 的 `message` 函数中,后续会考虑写入数据库,并添加管理函数进行统一的管理。
|
||||
|
||||
权限效验
|
||||
------------
|
||||
|
||||
在开发后台管理模板的过程中会涉及到权限效验,即访问控制。Pear Admin Flask 中提供了方便的函数用于进行权限效验。详情请查看 :ref:`权限验证模块` 章节。
|
||||
|
||||
.. _Schema 序列化:
|
||||
|
||||
Schema 序列化
|
||||
---------------
|
||||
|
||||
@@ -79,6 +86,12 @@ Schema 序列化
|
||||
# fields= ["id","name"] # 启动的字段列表
|
||||
# exclude = ["id","name"] # 排除字段列表
|
||||
|
||||
.. note::
|
||||
|
||||
更多参数可以参考官方文档对其的解释,链接如下:`SQLAlchemyAutoSchema <https://marshmallow-sqlalchemy.readthedocs.io/en/latest/api_reference.html>`_
|
||||
|
||||
|
||||
.. _与 layui 的数据格式同步:
|
||||
|
||||
与 layui 的数据格式同步
|
||||
------------------------------
|
||||
@@ -133,6 +146,8 @@ Schema 序列化
|
||||
|
||||
**需要注意的是,如果不提供 page 和 limit 则该函数必须在视图函数中使用,该函数会自动获取 GET 请求中的 limit 和 page 参数构成查询。**
|
||||
|
||||
:param page: 页码
|
||||
:param limit: 页数据个数
|
||||
:return: 返回分页对象。
|
||||
|
||||
**示例:**
|
||||
@@ -144,25 +159,30 @@ Schema 序列化
|
||||
return model_to_dicts(schema=MailOutSchema, data=mail.items)
|
||||
|
||||
|
||||
.. method:: layui_paginate_json(schema: Schema)
|
||||
.. method:: layui_paginate_json(schema: Schema, page=None, limit=None)
|
||||
|
||||
分页查询并返回 JSON 格式数据,适用于 Layui 表格。
|
||||
分页查询并通过 Marshmallow Schema 类 转化为 JSON,适用于 Layui 表格。
|
||||
|
||||
:param schema: Marshmallow Schema 类。
|
||||
:param page: 页码
|
||||
:param limit: 页数据个数
|
||||
:return: 返回包含序列化数据、总数、当前页码和每页条数的元组。
|
||||
|
||||
|
||||
.. method:: layui_paginate_db_json()
|
||||
.. method:: layui_paginate_db_json(page=None, limit=None)
|
||||
|
||||
分页查询并返回数据库原始数据的 JSON 格式,适用于 Layui 表格。
|
||||
|
||||
:return: 返回包含序列化数据和总数的元组。
|
||||
:param page: 页码
|
||||
:param limit: 页数据个数
|
||||
:return: 返回包含序列化数据(列表)、总数、当前页码和每页条数的元组。
|
||||
|
||||
**示例:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
db.query(User.name).layui_paginate_db_json()
|
||||
>> db.session.query(Gift.id, Gift.key).layui_paginate_db_json()
|
||||
([{'id': 0, 'key': 'myTestCode'}, {'id': 1, 'key': 'DisableCode'}], 2, 1, 10)
|
||||
|
||||
|
||||
进行字段构造
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
* 优化代码结构,新增函数 `normal_log` 减少代码复用
|
||||
* 添加了新插件的事件
|
||||
* 修改了验证码生成路由
|
||||
* 为 ModelFilter 增加了字符转义
|
||||
|
||||
已知问题以及解决方式
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reference in New Issue
Block a user