大家618好呀!
这个系列好像更新进度很慢了,最近主要以水文为主。今天天气比较差,打算来点干货。恰巧饭哥也在坐台
(坐在讲台),所以我就不客气了!
# 编写环境管理页面
之前的篇幅里面说过,用例没有根据环境
去划分它的归属,其实这样也是不太合理的,毕竟不同的环境需要的数据可能不一致,并不一定能编写完全通用的用例。所以今天我打算从这个页面下手,手把手
教大家编写一个完整的CRUD功能(带前端+联调)。后面前端的部分,如果大家不感兴趣的话,只看后端
就好了。因为会有一系列的截图!看完不说半条命,那半小时肯定没了。
有的人可能不理解,环境不就是那几套吗?测试/预发/线上等等。是的,但是各个公司会叫不同的名字,所以我们为了制作一款大众且通用的工具,所以提供他们维护自己环境的功能。
# CRUD/CURD是什么
经常逛脉脉
的同学肯定都知道,经常能看到CURD CRUD CRUD boy这样的词汇。直白点说,curd指的就是增删改查,我们今天的内容就是这个,且听我一一道来。
增
公司最近开了一个新的环境:fat2,我们系统得维护起来,所以我们需要新增一条
fat2
环境的数据。删
公司又不用这个环境了,想去掉这个环境,免得混淆视听。
改
fat2环境公司觉得不好听,公司决定改成fat-beta。
查
公司现在有100多种环境,太多了,需要查询是否有fat77这个环境。
没错,其实很多开发做的功能都与这个接近。大部分时间都是在做一些crud的工作。上述是细化到我们这一次环境管理的功能点,如果你觉得难的话,耐心看完(抄完)你就不会觉得难了。
# 明确思路
首先我们先要搞清楚本次需求,其实需求很简单,就是想给用户提供自定义环境名的功能。那么我们是不是在数据库需要存储这些环境
的信息。最基本的信息需要哪些呢?我这里列一下:
# 数据表准备
核心字段:
- 环境名称
比如fat uat pro (是否能重复?那肯定是不能重复的呀!)
- 环境备注
比如fat是测试环境啥的啥的,其实也可以不要,但是给你一个选项,万一你需要添加一些说明呢
基本字段:
- 创建人是谁
- 修改人是谁
- 什么时候创建的
- 什么时候更新的
- 被删除了木有
# 功能需求准备
环境可以通过名字模糊查询
可以编辑,编辑后会更新编辑时间
可以添加
可以删除
功能一拆分,是不是特别简单了,其实需求没多少,后端可能1小时不到就搞定了,前端还是费时间的。
# 开始行动
# 定义model
既然表的字段不多,数据也不复杂,那么我们可以先定义model。
内容不难,熟悉sqlalchemy就更轻松了。目的是为了映射一个数据表(环境表)的类
。
tablename 表示当前class对应的数据库表名,我们这里为了区分,统一加上pity_
前缀。
后面的内容就是对字段的定义以及初始化类。
# 生成model
为了能自动生成数据表,我们需要在dao/init.py里面import这个model类。
# 定义Pydantic的schema
这一步的目的很简单,就是为了做参数校验,比如有的哥们很坏,他创建环境的时候,直接给你来个空字符串或者空格字符串的环境名
,所以遇到这样的人,我们要鄙视他,在数据插入数据库之前就检测到,并给他沉重打击。这里熟悉pydantic的同学,都了解,不熟悉的话建议去阅读以下Pydantic的官方文档,或者照着抄就是了。
大体意思就是定义了一个Environment的表单,里面有3个参数: id、name、remarks,对应的就是id,环境名和备注。其中name不能为空,备注随便你,写不写都行,id你也可以不传,不传就默认为None。
# 编写Dao层(与db交互的核心内容)
- 新增环境
由于环境也算配置里面的一套内容,后续要和全局变量放到一起,所以我们给他归档到config目录下。
来看看这个新增环境
的功能都做了啥。
首先固定搭配,这个dddd(懂的都懂)。从数据库拿到一个session,类似于db.cursor()也类似于jdbc里面的getConnection。
with Session() as session:
接着,根据我们的业务逻辑,虽然咱们数据库设置name为unique,我们还是先查询是否有这个name并且deleted_at=None(删除时间=None 代表未被删除的)的环境数据,如果有的话,我们不继续新增了,返回一个提示信息
就行。
否则呢,我们就把数据插到数据库并用session进行提交。如果中途发生异常,我们就该记录日志的记录日志,该返回错误的返回错误。如果没啥问题就返回None,代表大吉大利,相安无事
。
- 编辑环境
套路都是一样的,甚至代码也很接近。但是注意到有个区别,这里调用了update_model方法,这个方法是干啥的哩?
我们知道,更新一条数据,我们除了需要更改的字段以外,还得有更新人和更新时间这2个字段跟着修改。所以我们编写了一套通用的方法,把你传入的data数据,改到query里面,顺便改写一下更新时间和更新人。
not_null指的是,只更新非空字段,比如我本来有个数据是abc,你传None进来,我给他改成None了,这样可能会有误会。这里借鉴了gorm,只更新非null的字段。dist代表目标,source代表数据源。
- 删除环境
其实删除环境,我们这里做的是软删除
,所以其实它还是一个update的操作。我们通过id找到对应的数据,把它的deleted_at改成当前时间即可,顺便改一下更新人(留下罪证)。
- 查询环境
注意,我这儿用的是list_env名称,为啥不是search呢?这里随你的便,我其实是把list和search合体了一下。可以看到我的name参数,如果不是None的时候,它就是个search动作,否则它是一个list操作。
search的根本条件是,deleted_at == None,也就是说不管怎么样,咱们都得保证数据没被删除。
if name:
search.append(Environment.name.ilike("%{}%".format(name)))
2
如果name有传,或者不为""也不为None,那么我们加上对name的模糊查询,等价于where name ilike xxxxx
。
最后就是自动分页了,我们查出来的数据很多的话,不可能全部做展示,所以我们需要进行分页。page和size指的就是当前页码
和一页多少条数据
。
最后咱们通过created_at进行倒序排列,也就是说最新创建的在最前面
。返回一个total,就是本次查询查到了多少条数据,以便于前端进行展示。
# 编写router
router层很简单,通过ApiRouter新建路由,并把list/insert/update/delete方法注册到路由。可以看到基本上代码量很少,属于调用dao层的核心方法,根据返回结果来返回http response。
值得注意的是,我没有用restful api去写接口,所以udpate/insert都是post请求,其他是get请求。(未出现put delete)
# 注册router
最后把刚才编写的router注册到app(pity)就可以了。
剧透下效果:
本期内容就到这里了,不水文虽然少点乐趣,但是hin充实呀!下一期以前端怎么curd为主,各位观众姥爷,拜了个拜!