该文原创为新潮质量保障技术团队中的 “上进的中年软件测试从业者”,用于技术交流分享
有一周没更新了,过去三天一直在处理日期控件无法使用的问题。实际上半年前第一次使用 MongoEngine 的 Datefield 的数据类型去生成日期控件就是有问题的。当前采用了 Datetimefield 数据类型来取代,所产生的直接影响是,前端使用控件输入的数据为时间格式(%Y-%m-%d %H:%M:%S),对于以日期结算的业务来说使用起来非常别扭。
MongoEngine 支持的 Datefield 继承了 Datetimefield,从常识来说既然 Datetimefield 可以用,Datefield 是没道理不能用的。
从表象来看,使用 Datefield 在提交表单时的报错信息为 “ValueError: Form does not have field startDate”, 翻译过来就是日期格式的 startDate 字段并没有在 form 里面找到。
通过 Debug 模式断点的方式去看(断点位置还是通过常规的由外到内的过程来打),发现生成的 form 里面确实没有 startDate 字段。
通过上面的调用过程,根本找不到 form 的生成过程以及 startDate 为什么不存在的原因。接下来根据使用过程中的经验来继续跟进。
继续通过 debug 模式跟进,并将断点打在 View 调用 Model 的地方, 并重启服务(切记一定要重启服务,Model 在 View 层的加载过程是在服务启动过程中)。最终通过 create_form->_create_form_class->get_create_form->get_form->scaffold_form->get_form 一路追踪到 mongoengine.form, 在里面发现了可能存在问题,以及大量的 mongoengine 的 orm 转换操作(转换为底层可以统一接受的数据模型)。在问题解决过程中通过网络查了很多资料,得到了一个比较宝贵的信息,那就是 flask_admin.form 里面根本没有 Datefield 这种数据类型,这个后续会介绍。
根据上图的断点信息和后续那个生硬的判断,一下就能看出,如果 mongoengine 转换 flask_admin.form 可接受的 orm 失败,自动抛弃。
现在没办法 FQ,就不去开源社区提 bug 了。后续可以 FQ 了再跟进一下这个问题的后续处理。
事还是要做,该实现的功能还是需要实现。在网上查到的大量资料经过删选后,所剩下可用的寥寥无几,但是却提供了一定的思路。
通过 flask_admin View 层的控件属性重写功能 form_widget_args 来实现。
通过 F12 抓包,确定通过 Datetimefield 生成的 element 信息如下:
<input class="form-control" data-date-format="YYYY-MM-DD HH:mm:ss" data-role="datetimepicker" id="startDate" name="startDate" required="" type="text" value="">
通过网上的资料,同样存在 datepicker 的 data-role。这里我们就可以修改 data-role 属性为 dateepicker,data-data-format 的类型为"YYYY-MM-DD"
修改后生成的 element 信息:
<input class="form-control" data-date-format="YYYY-MM-DD" data-role="datepicker" id="startDate" name="startDate" required="" type="text" value="">
这里我们发现,确实生效了,但是点击保存后新的问题出现了。
根据错误信息提示,追踪到 wtform.field。
我们发现:
- 虽然强制更改了控件的属性为 datepicker, 但是数据本身的格式还是 Datetimefield。
- 同时被 flask_admin.form 强制转换 orm 过程中被正确转换为可接受的 Datetimefield 字段。
- 而且flask_admin.form 是继承了 wtform.
- 所以这里的校验还是调用了 DaTimeField 的校验格式'%Y-%m-%d %H:%M:%S'。
根据如上的调研,通过 flask_admin.form 的方式提供的 Datetimefield 类型来实现,flask_admin.form 没有支持纯 Datefield 的原因是 Datetimefield 可以通过 format 属性来修改控件为纯日期选择器。
最开始的想法是在 model 的数据格式定义中直接把 startDate 字段定义为 flask_admin.form.Datetimefield, 然后根据 format 来控制纯日期格式的选择。但是根据上面的调研,mongoEngine 与 flask_admin.form 需要通过框架的 orm 转换才能被接受,所以在 MongoEngine.Document 里面去定义 flask_admin.form.Datetimefield 的数据类型,是不能被接受的。
-利用第一次解决过程中的 form_widget_args 的更改控件属性的特性,来解决前端日期选择器的问题。
-利用 form_extra_fields 的重写 model 层定义的字段类型功能,来解决校验不通过的问题;并重新 format 为"%Y-%m-%d"属性来通过 datepicker 控件产生的日期数据的最终校验。
实事求是的讲这个问题之前有调研过,但是最终放弃了。重新拾起来需要信息,否则信心会再一次被打击。这个问题的追进差不多打了 20 个断点,解决方案组合尝试多达十多次。当然收获也是比较大的,我可以开始尝试参与到开源建设中去。再次感谢您的耐心阅读,探究的路上注定孤独,但愿与你为伴!