大道至简,新一代企业应用无栈开发

平台之上,一种语言,可视化、脚本化、全端一体化开发

表单处理

docutils document without title

如何通过脚本来展示和处理表单。

1   基本表单处理流程

1.1   图形化定义表单

在软件包里面,采用可视化的方式来定义表单。具体参照表单流程的定义。

表单通过一个包含软件包名字的全面来引用,比如 zopen.tasks:task 表示在软件包 zopen.task 中定义的一个叫做 task 的软件包:

form_obj = root.packages.get_form_obj('zopen.tasks:task')

1.2   表单展示

下面的例子中,表单的定于位于zopen.task应用软件包中task表单,最终表单遮罩显示,会有一个保存按钮,点击后ajax提交后到zopen.test软件包的test1脚本处理:

form = ui.form('zopen.task:task')\
                    .button('save', '保存')\
            .on('submit', context, request, 'zopen.test:test1')

view.modal(form)

其中:

  1. ui.form() 里面直接转递表单的全名
  2. 通过 on('submit', ...) 指定表单提交处理脚本

1.3   获取表单提交数据 get_submitted

用户提交表单,这时候可以对提交表单数据处理。数据存放在 request.form 中:

title = request.form['title']  # title字段
description = request.form['description']  # description字段

request.form 中的值,会根据字段类型,自动对请求内容做初步的类型转换,比如转换整数、布尔、列表等类型。

这些数据还需要进行表单输入合法性校验,有些数据还需要做清理转换。

对于表单定义:

form = ui.form('zopen.task:task')\
                    .button('save', '保存')\
            .on('submit', context, request, 'xxx.xx:xxx')

得到表单提交的内容:

button, errors, result = form.get_submitted(request.form)

返回值:

  • button:点击的按钮
  • errors:各个字段的错误信息
  • result:表单的数据

对结果进行处理:

# 没有点击button的情况,表示首次访问,弹出展示这个表单
if not button:
    return view.modal(form)

# 提交表单,发生校验错误
if errors:
    return view.message(errors.values()[0], 'error')

# 如果点击了save按钮
if button == 'save':
      title = result['title']
      ....

get_submitted 更完整的调用:

form.get_submitted(request_form, fields=None, check_required=True, keep_multiple=False, pid=None, **options)

其中:

- fields: 仅仅处理那几个字段
- check_required: 是否需要判断必填条件
- keep_multiple: 对于人员、表单、文件夹等选择组件,当设置为单选的时候,是否和多选一样返回list。默认为True
- pid:如果有需要分用户存储字段,这个是当前用户id
- options: 动态计算需要的额外参数

2   深入表单渲染

2.1   使用表单对象

除了表单全名,可以传递一个表单对象:

form = ui.form(root.packages.get_form_obj('xxx.xxx:xxx'))

或者扩展属性对象:

form = ui.form(root.packages.get_mdset_obj('xxx.xxx:xxx'))

2.2   自定义表单标题

使用自己的表单标题/描述信息:

form = ui.form(FORM_NAME,
        title="请输入...",
        description="....")

2.3   多个表单按钮

支持多个按钮,并可指定按钮风格。

比如添加一个使用危险风格的按钮:

.button('remove', '删除', klass='danger')

也可以自定义css:

.button('remove', '删除', style="background: yellow")

2.4   多个表单字段组合

通常搜索的时候,需要将表单基础属性和多个扩展属性,组合在一起搜索或者批量编辑。

可以组合组合多个表单定义的字段:

form = ui.form([form_obj_1['name_a'], form_obj_1['name_b'], mdset_obj_2['name_c'], mdset_obj_3['name_d']])

使用 form.get_submitted 得到的result形式是:

{'name_a':value_a,
 'name_b':value_b,
 'xxx.xx:xx:name_c':value,
 'xxx.xx:xx:name_d':value, }

多个表单字段组合,也支持 search 模式。

3   深入表单字段

3.1   更完整的例子 fields

form = ui.form(FORM_NAME, action='')\  # 表单的标题和action
            .fields(data={'title':'the title'}, errors=errors)\
            .hidden({'name1':'value1', 'name2':'value2'})\  # 隐藏变量
            .button('save', '保存')\ # 增加一个按钮
            .on('submit', '@zopen.sales:test')  # 表单,而不是普通的表单

气质 fields() 完整API:

form.fields(data={},
     template=None,  # 模板
     edit_fields=None,  # 可编辑的字段
     omit_fields=(),    # 忽略的字段
     errors={},         # 字段错误信息
     **options):
  • data: 存放各字段初始值, 得到表单定义的初始值,是一个dict: {field_name:value}
  • edit_fields 需要编辑的字段,如果不是编辑字段,则自动渲染为只读形式,是一个list: [field_name]
  • omit_fields 表单中需要忽略的字段, 是一个 list: [field_name]
  • errors 各字段的错误信息,是一个dict: {field_name:error_info}
  • template: 个性化的模板, 具体见下面说明
  • options: 动态计算需要的额外参数

3.1.1   默认值

可以直接输入表单初始数据:

ui.form(FORM_NAME).fields(data={'title':'测试', 'description':'lala'}, )

3.1.2   只读字段

哪些字段是可以编辑的,否则只读线上:

ui.form(FORM_NAME).fields(edit_fields=[‘title’])

3.1.3   忽略的字段

隐藏哪些字段:

ui.form(FORM_NAME).fields(omit_fields=[‘description’])

3.1.4   字段的出错信息

表单各字段的出错信息:

ui.form(FORM_NAME).fields(errors={‘title’:’必须填写!’})

3.2   隐藏字段 hidden

表单中可以增加隐藏字段,用于传递参数:

ui.form(FORM_NAME).hidden({'aa':'121', 'bb':'sadfa'})

3.3   自定义布局模板 layout

表单支持三种布局:

  • table: 左右布局,默认使用:

    form.layout('table')
    
  • div:也可以改为上下布局:

    form.layout('div')
    
  • inline: 单行精简布局:

    form.layout('inline')
    

如果以上几种常用布局都不适合你,可以完全自定义模板:

form.template('用户名: $username密码: $password')

模板采用html,其中 $name 表示name字段

3.4   传递字段表达式参数 options

有些表单字段的某些参数是需要动态计算的,比如人员选择,文件选择、表单选择,需要传入context/request才能工作,因此表单必须加上:

ui.form(FORM_NAME).fields(context=context, request=request)

再如,选择字段的可选值,可以传入:

ui.form(FORM_NAME).fields(select_options=[‘a’, ‘b’])

这样,可以在表单的选择字段可选值表达式里面直接使用select_options这个变量

4   深入表单对象

得到form对象:

form_obj = root.packages.get_form_obj(FORM_NAME)

4.1   表单默认值 get_defaults

得到表单的初始值:

data = form_obj.get_defaults(fields=None, **options)

4.2   表单的校验 validate

也可以通过 validate 方法主动检查一下用户输入参数是否合法:

errors = form_obj.validate(result, storage=None, context=context, request=request, view=view, container=container)

其中:

  • fields 是需要校验的字段
  • result 是准备保存的数据
  • storage 之前保存的数据,可以用判断是否有修改

4.3   表单的保存触发脚本

可以直接调用 on_update 脚本,返回值错误信息:

errors = form_obj.on_update(context=context, request=request, view=view, container=context.parent, old_storage = {})
if errors:
    import transaction
    transaction.abort()
    view.message(errors.values(0), 'error')

返回值 errors: 错误值: {field_name:error_info}

如果update传递context参数,调用之后,会自动更改context的修改时间。

4.4   渲染表单字段

得到字段:

field_obj = form_obj[field_name]

渲染表单编辑字段:

field_obj.render_edit(value, context=context, request=request, container=container, **kw)

渲染网页查看方式:

field_obj.render_value(value, context=context, request=request, container=container, **kw)

文本方式方式输出:

field_obj.render_text(value, context=context, request=request, container=container, **kw)

5   组合搜索表单 search_form

上面的表单通常用于新建和编辑,如果需要搜索,表单各个字段通常用于构造条件,比如:

  • 文本框:单行文本,全文匹配
  • 日期:范围,支持(本周、本月切换)
  • 整数:范围
  • 小数:范围
  • 选择框:包含其一、全部包含

所以表单在搜索的时候,实际每个字段变成包含多个搜索条件输入项的组合字段。

5.1   渲染组合搜索表单

如下渲染搜索表单:

form = ui.search_form(FORM_NAME)

其中:

  1. ui.form 参数类似,第一个参数更灵活:

    form = ui.search_form(form_obj)  # 传递表单对象
    form = ui.search_form(mdset_obj)  # 传递扩展属性
    form = ui.search_form([form_obj1, form_obj2, mdset_obj1, mdset_obj2]) # 多个表单和扩展属性对象的列表
    
  2. 输出的是一个包含各个字段组合搜索条件的新表单

  3. 新表单各字段的子字段,实际是另外一个表单,也可定制

5.2   序列化搜索条件 get_submitted

表单每个字段,使用搜索模式进行渲染,可得到用户输入的数据:

error, button, result = form.get_submitted(request.form)

5.3   转为搜索引擎条件 gen_query_json

最终将result转换为搜索条件进行搜索:

query_json = form.gen_query_json(result=result)
qs_result = QuerySet().query(query_json)