使用 Python 从 0 开发属于你的个人管理系统 29 | 对于复杂数据应该如何处理?

你在开发的过程中,并不总是处理简单的数据交互,有时候你的业务需要得到比较特殊的数据,那么就无法单纯的从 model 中调用简单的「增删改查」,你还需要更近一步的分析,而这里的分析和实现,也是有一定的技巧的。

比如之前我给你演示的「任务拆分」功能,一个大计划下有多个小计划,每一个小计划都有一个是否完成的状态:

那么在获取计划列表的时候,我们想要只获取那些未完成的大计划数据,应该怎么获得呢?如果这时我们直接从代码下手,可能会晕头转向。

首先我们来思考一个问题,想要得到一个未完成的大计划列表,我们就要得到每一个大计划是否完成,而想要知道每个大计划是否完成,我们就得知道每个大计划下的所有子计划是否都完成。因为只有所有的子计划都完成了,才说明这个大计划是完成了的。

那么我们很快就把目光的焦点转向:小计划。

就从最简单的开始,查询当前用户的所有小计划数据:

select * from sub_plan where user_id = 12;

这张表的记录中,有个 plan_id 字段,就是对应的大计划的 id。

通过 group by, 就可以得到所有大计划的 id:

select plan_id from sub_plan where user_id=12 group by plan_id;

那么在这里判断哪些 plan_id 的都是完成的不就好了么?

我们之前就定义过,小计划的 is_done 字段是来判断它的完成状态的,0表示未完成,1 表示已经完成。

那么是不是把一个大计划下的所有子计划的 is_done 加起来,再除以所有子计划的数目,如果小于 1 ,那么就是说明这个大计划是未完成的。

那么就可以这么查询,得到未完成的大计划 id:

select plan_id from sub_plan where user_id=12 group by plan_id having (sum(is_done)/count(*) < 1)

现在,我们可以通过 sub_plan 这张表得到「所有大计划的id」以及「所有未完成的大计划id」。那我们就可以根据它们作为「条件」,在大计划 plan 表中做查询。

有一点要注意的是,我们在创建大计划的时候,有些我们可能还没有创建小计划(sub_plan没有记录):

所以这个大计划也是属于未完成状态的,那么这时候的大计划 id 就不在 『通过 sub_plan 这张表得到「所有大计划的id」』中。

现在要获取当前用户所有未完成的大计划列表就可以这样查询:

select * from plan where user_id=12 and (id not in (select plan_id from sub_plan where user_id=12) or id in(select plan_id from sub_plan where user_id=12 group by plan_id having sum(is_done)/count(*) < 1)) order by create_at desc;

也就是通过查询大计划表,只要 id 在小计划表中不存在的,以及在小计划表中发现没有「所有子计划」都完成的,那么就说明这个大计划没有完成。

这样通过最小单位的分析,反向获得查询的子条件,从而获得我们想要的数据。就比较容易一些。

那么接下来,就要将这样的查询语句,通过 sqlalchemy,用面向对象的方式表达它们。

按照我们刚刚的思路,先把子条件写出来。

在 sqlalchemy 中写子查询,可以调用它的 subquery 方法,像这样获取子计划表中的大计划id:

sql 是这样的:

select plan_id from sub_plan where user_id=12 group by plan_id;

那么代码就是这样:

subquery_all_subplan = db.session.query(SubPlan.plan_id) \                
      ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦.filter(SubPlan.user_id == current_user.id) \  
      ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦.subquery()  

获取子计划表中所有未完成的大计划id:

本文隐藏内容 登陆 后才可以浏览
运行起来就是得到所有未完成的数据了:

所谓大事化小,小事化了,就是这么个理。

发表回复