actiontype:method calls render() without args, losing search params. Changed to actiontype:script with getWidgetById + await tbl.render(params) which correctly receives merged InlineForm submit data as params argument.
596 lines
15 KiB
Python
596 lines
15 KiB
Python
data_browser_tmpl = """
|
|
{
|
|
"widgettype":"VBox",
|
|
"options":{"height":"100%","width":"100%"},
|
|
"subwidgets":[
|
|
{% if data_filter and data_filter.fields %}
|
|
{
|
|
"widgettype":"InlineForm",
|
|
"id":"{{tblname}}_search",
|
|
"options":{
|
|
"css":"card",
|
|
"padding":"8px",
|
|
"show_label":false,
|
|
"submit_label":"搜索",
|
|
"submit_css":"primary",
|
|
"fields":[
|
|
{%- for f in data_filter.fields %}
|
|
{%- if f.uitype == 'code' %}
|
|
{{json.dumps(filter_fields[loop.index0], ensure_ascii=False)}}{% if not loop.last %},{% endif %}
|
|
{%- else %}
|
|
{"name":"{{f.field}}","uitype":"{{f.uitype}}","placeholder":"{{f.title}}","cwidth":15}{% if not loop.last %},{% endif %}
|
|
{%- endif %}
|
|
{%- endfor %}
|
|
]
|
|
},
|
|
"binds":[{
|
|
"wid":"self",
|
|
"event":"submit",
|
|
"actiontype":"script",
|
|
"target":"{{tblname}}_tbl",
|
|
"script":"var tbl = bricks.getWidgetById('{{tblname}}_tbl', bricks.app.root); if(tbl) await tbl.render(params);"
|
|
}]
|
|
},
|
|
{% endif %}
|
|
{
|
|
"id":"{{tblname}}_tbl",
|
|
"widgettype":"Tabular",
|
|
"options":{
|
|
"width":"100%",
|
|
"height":"100%",
|
|
{% if not notitle %}
|
|
{% if title %}
|
|
"title":"{{title}}",
|
|
{% else %}
|
|
"title":"{{summary[0].title}}",
|
|
{% endif %}
|
|
{% endif %}
|
|
{% if description %}
|
|
"description":"{{description}}",
|
|
{% endif %}
|
|
{% if toolbar %}
|
|
"toolbar":{{json.dumps(toolbar, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
"css":"card",
|
|
{% if editor %}
|
|
"editor":{{json.dumps(editor, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
{% if not noedit %}
|
|
"editable":{
|
|
{% if new_data_url %}
|
|
"new_data_url": "{{new_data_url}}",
|
|
{% else %}
|
|
"new_data_url":{%- raw -%}"{{entire_url('add_{%- endraw -%}{{summary[0].name}}{%- raw -%}.dspy')}}",{%- endraw %}
|
|
{% endif %}
|
|
{% if delete_data_url %}
|
|
"delete_data_url": "{{delete_data_url}}",
|
|
{% else %}
|
|
"delete_data_url":{%- raw -%}"{{entire_url('delete_{%- endraw -%}{{summary[0].name}}{%- raw -%}.dspy')}}",{%- endraw %}
|
|
{% endif %}
|
|
{% if update_data_url %}
|
|
"update_data_url": "{{update_data_url}}"
|
|
{% else %}
|
|
"update_data_url":{%- raw -%}"{{entire_url('update_{%- endraw -%}{{summary[0].name}}{%- raw -%}.dspy')}}"{%- endraw %}
|
|
{% endif %}
|
|
},
|
|
{% endif %}
|
|
{% if data_url %}
|
|
"data_url": "{{data_url}}",
|
|
{% else %}
|
|
"data_url":"{%- raw -%}{{entire_url('./get_{%- endraw -%}{{summary[0].name}}{%- raw -%}.dspy')}}",{%- endraw %}
|
|
{% endif %}
|
|
"data_method":"{{data_method or 'GET'}}",
|
|
"data_params":{%- raw -%}{{json.dumps(params_kw, indent=4, ensure_ascii=False)}},{%- endraw %}
|
|
"row_options":{
|
|
{% if idField %}
|
|
"idField":"{{idField}}",
|
|
{% endif %}
|
|
{% if checkField %}
|
|
"checkField":"{{checkField}}",
|
|
{% endif %}
|
|
{% if browserfields %}
|
|
"browserfields": {{json.dumps(browserfields, indent=4, ensure_ascii=Fasle)}},
|
|
{% endif %}
|
|
{% if editexclouded %}
|
|
"editexclouded":{{json.dumps(editexclouded, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
"fields":{{fieldliststr}}
|
|
},
|
|
{% if subtables_condition %}
|
|
{%- raw -%}{% {%- endraw %}if {{subtables_condition}} {%- raw -%} %}{%- endraw -%}
|
|
{% endif %}
|
|
{% if content_view %}
|
|
"content_view":{{json.dumps(content_view, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
{% if data_filter %}
|
|
"data_filter":{{json.dumps(data_filter, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
{% if filter_labels %}
|
|
"filter_labels":{{json.dumps(filter_labels, indent=4, ensure_ascii=False)}},
|
|
{% endif %}
|
|
{% if filter_title %}
|
|
"filter_title":"{{filter_title}}",
|
|
{% endif %}
|
|
{% if filter_icon %}
|
|
"filter_icon":"{{filter_icon}}",
|
|
{% endif %}
|
|
{% if subtables_condition %}
|
|
{%- raw -%}{% endif %}{%- endraw %}
|
|
{% endif %}
|
|
"page_rows":160,
|
|
"cache_limit":5
|
|
}
|
|
{% if bindsstr %}
|
|
,"binds":{{bindsstr}}
|
|
{% endif %}
|
|
}]
|
|
}
|
|
"""
|
|
get_data_tmpl = """
|
|
ns = params_kw.copy()
|
|
{% if logined_userid %}
|
|
userid = await get_user()
|
|
if not userid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userid}}'] = userid
|
|
ns['userid'] = userid
|
|
{% endif %}
|
|
{% if logined_userorgid %}
|
|
userorgid = await get_userorgid()
|
|
if not userorgid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userorgid}}'] = userorgid
|
|
ns['userorgid'] = userorgid
|
|
{% endif %}
|
|
debug(f'get_{{tblname}}.dspy:{ns=}')
|
|
if not ns.get('page'):
|
|
ns['page'] = 1
|
|
if not ns.get('sort'):
|
|
{% if sortby %}
|
|
{% if type(sortby) == type("") %}
|
|
ns['sort'] = '{{sortby}}'
|
|
{% else %}
|
|
ns['sort'] = {{json.dumps(sortby)}}
|
|
{% endif %}
|
|
{% else %}
|
|
ns['sort'] = 'id'
|
|
{% endif %}
|
|
{% if relation %}
|
|
ns['sort'] = '{{relation.outter_field}}_text'
|
|
{% endif %}
|
|
sql = '''{{sql}}'''
|
|
{% if not relation %}
|
|
filterjson = params_kw.get('data_filter')
|
|
if filterjson and isinstance(filterjson, str):
|
|
\ttry:
|
|
\t\tfilterjson = json.loads(filterjson)
|
|
\texcept (json.JSONDecodeError, TypeError):
|
|
\t\tfilterjson = None
|
|
# data_filter可能是CRUD字段定义({"fields":[...]}),不是过滤条件,忽略
|
|
if filterjson and isinstance(filterjson, dict) and 'fields' in filterjson:
|
|
filterjson = None
|
|
fields_str=r'''{{json.dumps(fields, indent=4, ensure_ascii=False)}}'''
|
|
ori_fields = json.loads(fields_str)
|
|
if not filterjson:
|
|
fields = [ f['name'] for f in ori_fields ]
|
|
filterjson = default_filterjson(fields, ns)
|
|
{% if logined_userorgid or logined_userid %}
|
|
# 确保 logined 过滤条件始终生效
|
|
if filterjson:
|
|
if not isinstance(filterjson, dict) or 'AND' not in filterjson:
|
|
filterjson = {'AND': [filterjson] if filterjson else []}
|
|
{% if logined_userorgid %}
|
|
filterjson['AND'].append({'field': '{{logined_userorgid}}', 'op': '=', 'var': '__logined_orgid__'})
|
|
ns['__logined_orgid__'] = userorgid
|
|
{% endif %}
|
|
{% if logined_userid %}
|
|
filterjson['AND'].append({'field': '{{logined_userid}}', 'op': '=', 'var': '__logined_uid__'})
|
|
ns['__logined_uid__'] = userid
|
|
{% endif %}
|
|
{% endif %}
|
|
filterdic = ns.copy()
|
|
filterdic['filterstr'] = ''
|
|
filterdic['userorgid'] = '${userorgid}$'
|
|
filterdic['userid'] = '${userid}$'
|
|
if filterjson:
|
|
dbf = DBFilter(filterjson)
|
|
conds = dbf.gen(ns)
|
|
if conds:
|
|
ns.update(dbf.consts)
|
|
conds = f' and {conds}'
|
|
filterdic['filterstr'] = conds
|
|
ac = ArgsConvert('[[', ']]')
|
|
vars = ac.findAllVariables(sql)
|
|
NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' }
|
|
filterdic.update(NameSpace)
|
|
sql = ac.convert(sql, filterdic)
|
|
{% endif %}
|
|
debug(f'{sql=}')
|
|
db = DBPools()
|
|
dbname = get_module_dbname('{{modulename}}')
|
|
async with db.sqlorContext(dbname) as sor:
|
|
r = await sor.sqlPaging(sql, ns)
|
|
return r
|
|
return {
|
|
"total":0,
|
|
"rows":[]
|
|
}
|
|
"""
|
|
data_new_tmpl = """
|
|
ns = params_kw.copy()
|
|
for k,v in ns.items():
|
|
if v == 'NaN' or v == 'null':
|
|
ns[k] = None
|
|
id = params_kw.id
|
|
if not id or len(id) > 32:
|
|
id = uuid()
|
|
ns['id'] = id
|
|
{% for f in confidential_fields or [] %}
|
|
if params_kw.get('{{f}}'):
|
|
ns['{{f}}'] = password_encode(params_kw.get('{{f}}'))
|
|
{% endfor %}
|
|
{% if logined_userid %}
|
|
userid = await get_user()
|
|
if not userid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userid}}'] = userid
|
|
{% endif %}
|
|
{% if logined_userorgid %}
|
|
userorgid = await get_userorgid()
|
|
if not userorgid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userorgid}}'] = userorgid
|
|
{% endif %}
|
|
{% if validation_rules %}
|
|
_validation_rules = json.loads(r'''{{validation_rules}}''')
|
|
import re as _re
|
|
_errors = []
|
|
for _fname, _rules in _validation_rules.items():
|
|
_val = params_kw.get(_fname, '')
|
|
if _val is None: _val = ''
|
|
_val = str(_val)
|
|
for _rule in _rules:
|
|
_rt = _rule.get('type', '')
|
|
_rm = _rule.get('message', _fname)
|
|
_rv = _rule.get('value')
|
|
if _rt == 'required':
|
|
if not _val or _val.strip() == '':
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt == 'minlength':
|
|
if _val and len(_val) < int(_rv):
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt == 'maxlength':
|
|
if len(_val) > int(_rv):
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt in ('min', 'max'):
|
|
if _val:
|
|
try:
|
|
_n = float(_val)
|
|
if _rt == 'min' and _n < float(_rv): _errors.append(_rm); break
|
|
if _rt == 'max' and _n > float(_rv): _errors.append(_rm); break
|
|
except (ValueError, TypeError):
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt == 'pattern':
|
|
if _val and not _re.match(_rv, _val):
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt == 'email':
|
|
if _val and not _re.match(r'^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$', _val):
|
|
_errors.append(_rm)
|
|
break
|
|
elif _rt == 'number':
|
|
if _val:
|
|
try: float(_val)
|
|
except (ValueError, TypeError): _errors.append(_rm); break
|
|
if _errors:
|
|
return {"widgettype":"Error","options":{"title":"Validation Failed","cwidth":16,"cheight":9,"timeout":3,"message":"; ".join(_errors)}}
|
|
{% endif %}
|
|
db = DBPools()
|
|
dbname = get_module_dbname('{{modulename}}')
|
|
async with db.sqlorContext(dbname) as sor:
|
|
r = await sor.C('{{summary[0].name}}', ns.copy())
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"title":"Add Success",
|
|
"timeout":3,
|
|
"message":"ok"
|
|
}
|
|
}
|
|
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Add Error",
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"timeout":3,
|
|
"message":"failed"
|
|
}
|
|
}
|
|
"""
|
|
data_update_tmpl = """
|
|
ns = params_kw.copy()
|
|
for k,v in ns.items():
|
|
if v == 'NaN' or v == 'null':
|
|
ns[k] = None
|
|
{% if logined_userid %}
|
|
userid = await get_user()
|
|
if not userid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userid}}'] = userid
|
|
{% endif %}
|
|
{% if logined_userorgid %}
|
|
userorgid = await get_userorgid()
|
|
if not userorgid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userorgid}}'] = userorgid
|
|
{% endif %}
|
|
{% for f in confidential_fields or [] %}
|
|
if params_kw.get('{{f}}'):
|
|
ns['{{f}}'] = password_encode(params_kw.get('{{f}}'))
|
|
{% endfor %}
|
|
{% if validation_rules %}
|
|
_validation_rules = json.loads(r'''{{validation_rules}}''')
|
|
import re as _re
|
|
_errors = []
|
|
for _fname, _rules in _validation_rules.items():
|
|
\t_val = params_kw.get(_fname, '')
|
|
\tif _val is None: _val = ''
|
|
\t_val = str(_val)
|
|
\tfor _rule in _rules:
|
|
\t\t_rt = _rule.get('type', '')
|
|
\t\t_rm = _rule.get('message', _fname)
|
|
\t\t_rv = _rule.get('value')
|
|
\t\tif _rt == 'required':
|
|
\t\t\tif not _val or _val.strip() == '':
|
|
\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\tbreak
|
|
\t\telif _rt == 'minlength':
|
|
\t\t\tif _val and len(_val) < int(_rv):
|
|
\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\tbreak
|
|
\t\telif _rt == 'maxlength':
|
|
\t\t\tif len(_val) > int(_rv):
|
|
\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\tbreak
|
|
\t\telif _rt in ('min', 'max'):
|
|
\t\t\tif _val:
|
|
\t\t\t\ttry:
|
|
\t\t\t\t\t_n = float(_val)
|
|
\t\t\t\t\tif _rt == 'min' and _n < float(_rv): _errors.append(_rm); break
|
|
\t\t\t\t\tif _rt == 'max' and _n > float(_rv): _errors.append(_rm); break
|
|
\t\t\t\texcept (ValueError, TypeError):
|
|
\t\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\t\tbreak
|
|
\t\telif _rt == 'pattern':
|
|
\t\t\tif _val and not _re.match(_rv, _val):
|
|
\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\tbreak
|
|
\t\telif _rt == 'email':
|
|
\t\t\tif _val and not _re.match(r'^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$', _val):
|
|
\t\t\t\t_errors.append(_rm)
|
|
\t\t\t\tbreak
|
|
\t\telif _rt == 'number':
|
|
\t\t\tif _val:
|
|
\t\t\t\ttry: float(_val)
|
|
\t\t\t\texcept (ValueError, TypeError): _errors.append(_rm); break
|
|
if _errors:
|
|
\treturn {"widgettype":"Error","options":{"title":"Validation Failed","cwidth":16,"cheight":9,"timeout":3,"message":"; ".join(_errors)}}
|
|
{% endif %}
|
|
|
|
db = DBPools()
|
|
dbname = get_module_dbname('{{modulename}}')
|
|
async with db.sqlorContext(dbname) as sor:
|
|
{% if logined_userid or logined_userorgid %}
|
|
ns1 = {
|
|
{% if logined_userorgid %}
|
|
"{{logined_userorgid}}": userorgid,
|
|
{% endif %}
|
|
{% if logined_userid %}
|
|
"{{logined_userid}}": userid,
|
|
{% endif %}
|
|
"id": params_kw.id
|
|
}
|
|
recs = await sor.R('{{summary[0].name}}', ns1)
|
|
if len(recs) < 1:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Update Error",
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"timeout":3,
|
|
"message":"Record no exist or with wrong ownership"
|
|
}
|
|
}
|
|
{% endif %}
|
|
r = await sor.U('{{summary[0].name}}', ns)
|
|
debug('update success');
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"title":"Update Success",
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"timeout":3,
|
|
"message":"ok"
|
|
}
|
|
}
|
|
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Update Error",
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"timeout":3,
|
|
"message":"failed"
|
|
}
|
|
}
|
|
"""
|
|
data_delete_tmpl = """
|
|
ns = {
|
|
'id':params_kw['id'],
|
|
}
|
|
{% if logined_userid %}
|
|
userid = await get_user()
|
|
if not userid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userid}}'] = userid
|
|
{% endif %}
|
|
{% if logined_userorgid %}
|
|
userorgid = await get_userorgid()
|
|
if not userorgid:
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Authorization Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"Please login"
|
|
}
|
|
}
|
|
ns['{{logined_userorgid}}'] = userorgid
|
|
{% endif %}
|
|
db = DBPools()
|
|
dbname = get_module_dbname('{{modulename}}')
|
|
async with db.sqlorContext(dbname) as sor:
|
|
r = await sor.D('{{summary[0].name}}', ns)
|
|
debug('delete success');
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"title":"Delete Success",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"ok"
|
|
}
|
|
}
|
|
|
|
debug('Delete failed');
|
|
return {
|
|
"widgettype":"Error",
|
|
"options":{
|
|
"title":"Delete Error",
|
|
"timeout":3,
|
|
"cwidth":16,
|
|
"cheight":9,
|
|
"message":"failed"
|
|
}
|
|
}
|
|
"""
|
|
|
|
check_changed_tmpls = """
|
|
is_checked = params_kw.get('has_{{relation.param_field}}')
|
|
debug(f'{params_kw=}, {is_checked=}')
|
|
dbname = get_module_dbname('{{modulename}}')
|
|
if is_checked == 'true':
|
|
ns = {
|
|
"id":uuid(),
|
|
"{{relation.param_field}}":params_kw.{{relation.param_field}},
|
|
"{{relation.outter_field}}":params_kw.{{relation.outter_field}}
|
|
}
|
|
db = DBPools();
|
|
async with db.sqlorContext(dbname) as sor:
|
|
await sor.C('{{tblname}}', ns)
|
|
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"title":"Success",
|
|
"message":"record add success",
|
|
"timeout":2
|
|
}
|
|
}
|
|
else:
|
|
ns = {
|
|
"{{relation.param_field}}":params_kw.{{relation.param_field}},
|
|
"{{relation.outter_field}}":params_kw.{{relation.outter_field}}
|
|
}
|
|
sql = "delete from {{tblname}} where {{relation.param_field}}=" + "${" + "{{relation.param_field}}" + "}$" + " and {{relation.outter_field}}=" + "${" + "{{relation.outter_field}}" + "}$"
|
|
db = DBPools()
|
|
async with db.sqlorContext(dbname) as sor:
|
|
await sor.sqlExe(sql, ns)
|
|
|
|
return {
|
|
"widgettype":"Message",
|
|
"options":{
|
|
"title":"Success",
|
|
"message":"delete record success",
|
|
"timeout":3
|
|
}
|
|
}
|
|
"""
|