实现一个对Django Models数据进行模拟的Mock方案
问题在开发过程中整个数据流向为爬虫抓取数据-数据中端进行数据清洗-入库Web端定义的业务表由于整个流程比较长而且由于爬虫开发的不稳定性以及数据统计的复杂度完整的开发往往不能完全异步进行因为最后面向业务的Web端需要等待清洗入库的数据进行测试。一般来说如果Web端需要的业务数据比较简单开发自测的时候都可以手动生成INSERT等SQL模拟假数据但是如果业务复杂的时候往往需要十余个Table联动的数据手动INSERT比较麻烦开发效率低。思考模拟数据的难处主要有涉及地方多如10多个表逐一写入对应数据表与表之间的对应关系如测试的时候需要从表1取10条数据从表2取这10条数据对应的一周内各日的数据一共10 * 7条编写测试SQL费事效率低缺少开箱即用的数据生成器为此需要有一款工具根据Django的models字段随机生成数据支持指定数据内容如指定数据的id方便联表查询的时候能够正确JOIN出结果支持filter、get等常用方法支持聚合查询无需写入数据库返回QuerySet方便开关Mock与测试真正数据之间任意切换实现将以上问题逐一分析随机生成对应类型数据Django的Models常用的数据类型有CharField、IntegerField、DateTimeField、TextField、DecimalField、DateField其余类型在开发中不常用因此先实现这几种类型的随机生成器CharFieldCharField对应Varchar和Char类型目标是在有提供选项的时候随机返回选项中的内容没提供选项的时候随机出0-max_length范围内的字符串因此采用英文字母进行随机即可。import string from random import choice, randint def charfield_generator(min_length0, max_length20, choices[]): if not choices: return .join(choice(string.ascii_letters) for i in range(randint(min_length, max_length))) else: return choice(choices)IntegerFieldIntegerField可以对应各类整数类型包括SmallInt、TinyInt等均可共用同一个生成器通过限制长度来控制返回值。def integerfield_generator(min_value, max_value, choices[]): if not choices: return randint(min_value, max_value) else: return choice(choices)TextFieldTextField在要求不严格的情况下也可以和CharField共用生成器。业务上一般超长的内容会使用TextField如文章正文。DatetimeFieldDatetimeField生成对应的时间对象考虑生成一个大于起始时间(start_dt)小于结束时间(end_dt)的datetime对象。def datetime_generator(start_dtdatetime.now(), end_dtdatetime.now()): dt_delta end_dt - start_dt return start_dt timedelta(secondsrandint(0, 24 * 3600 * dt_delta.days dt_delta.seconds))简单执行一下看看第一版效果for i in range(10): print(-------------- GENERATING SET %s -------------- % i) print(charfield_generator()) print(integerfield_generator(0, 1000)) print(datetime_generator(datetime.strptime(2018-09-01 00:00:00, %Y-%m-%d %H:%M:%S))) print(-------------- GENERATED SET %s -------------- % i, \n)-------------- GENERATING SET 0 -------------- I 196 2018-09-30 13:22:31 -------------- GENERATED SET 0 -------------- -------------- GENERATING SET 1 -------------- XBFGGdpVwmlMMbCT 168 2018-11-16 09:02:16 -------------- GENERATED SET 1 -------------- -------------- GENERATING SET 2 -------------- ZgU 293 2018-12-04 08:44:08 -------------- GENERATED SET 2 -------------- -------------- GENERATING SET 3 -------------- TsUkylUiC 791 2018-10-01 03:48:16 -------------- GENERATED SET 3 -------------- -------------- GENERATING SET 4 -------------- IusHQZsKYFtKi 909 2019-04-22 02:02:27 -------------- GENERATED SET 4 -------------- -------------- GENERATING SET 5 -------------- ScRcj 505 2019-02-21 16:16:51 -------------- GENERATED SET 5 -------------- -------------- GENERATING SET 6 -------------- OLmbMrZImnvaF 500 2018-12-24 22:20:47 -------------- GENERATED SET 6 -------------- -------------- GENERATING SET 7 -------------- rNaRvAYSgxVzwLAe 664 2019-08-01 12:43:00 -------------- GENERATED SET 7 -------------- -------------- GENERATING SET 8 -------------- rtLks 532 2019-03-14 07:38:53 -------------- GENERATED SET 8 -------------- -------------- GENERATING SET 9 -------------- oIDFdOUKs 700 2018-09-21 19:59:06 -------------- GENERATED SET 9 -------------- [Finished in 0.2s]有了模拟数据之后需要做几件事将假数据映射到对应的Models上实例化成QuerySet由多个QuerySet组成iterative的QuerySet List需要支持指定QuerySet List长度因此可以实现计算页数、分页等操作def model_generator(models, length): : models Models.model : : length int : : rtype QuerySet List : return []最佳实践pass