自动化工具 新潮测试平台 - 被 flask_admin 无情抛弃的 MongoEngine.Datefield

池小波 for 新潮测试技术 · 2020年02月28日 · 2084 次阅读

该文原创为新潮质量保障技术团队中的 “上进的中年软件测试从业者”,用于技术交流分享

开篇​

有一周没更新了,过去三天一直在处理日期控件无法使用的问题。实际上半年前第一次使用 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 模式跟进

通过 Debug 模式断点的方式去看(断点位置还是通过常规的由外到内的过程来打),发现生成的 form 里面确实没有 startDate 字段。

通过上面的调用过程,根本找不到 form 的生成过程以及 startDate 为什么不存在的原因。接下来根据使用过程中的经验来继续跟进。

View 到 Model 调用关系追踪

继续通过 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 来实现。

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 不支持 Datefield 的原因

根据如上的调研,通过 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 个断点,解决方案组合尝试多达十多次。当然收获也是比较大的,我可以开始尝试参与到开源建设中去。再次感谢您的耐心阅读,探究的路上注定孤独,但愿与你为伴!

共收到 0 条回复 时间 点赞
1楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册