0O00--计算型字段的搜索方法
就拿一个比较常见的需求举例吧。如果我想跟进某个单据的进度,想筛选出距其创建起1天内,2天内,7天内的所有单据,怎么办?
懒得废话了。
方案1:
days = fields.Integer() + 定时任务每天+1
方案2:
def _compute_days(selfs):
_today = date.today()
for self in selfs:
self.days = (_today - self.create_day).days
days = fields.Integer(compute='_compute_days',)
因为系统无法感知到每一天的时间变化,所以就就形成了这样一个非储存的计算型的字段。
好吧,其实这个字段建立之初就只是为了用于筛选,我们就去看看非储存的计算型字段怎么被应用于搜索视图吧。
def _search_days(cls, operator, value):
operator_lambda = {
'>': lambda s: s.days > value,
'>=': lambda s: s.days >= value,
'=': lambda s: s.days == value,
'<': lambda s: s.days < value,
'<=': lambda s: s.days <= value,
}
selfs = cls.search([]).filtered(operator_lambda[operator])
return [('id','in',selfs.ids)]
为这样非储存的字段构建了一个支持比较的搜索方法,是odoo为我们提供的标准机制。
但问题是,这样的方法对吗?好吗?跟我们该干的事吻合吗?
回顾一下search属性的本意吧。
已知现在在业务层需要对days做筛选,而数据库并不理解这个表的days是什么东西,从而需要odoo后端在中间做过渡,或许转义更贴切一些。
重新再来看看这个search方法,大概率是可行的,走的通的,不够好的。
因为[('id','in',selfs.ids)]这样的返回值,早就脱离了转义的范畴。
domain之所以被设计成3元元组的列表结构,就是为了体现其中每一个条件的业务含义,不然倒不如直接接收若干个ids的合集,用来取交集并集补集不是更直接一些?
具体来看,这个搜索方法并不在乎业务,理论上可以适用于任何字段的搜索需求,完全是依赖其compute方法执行后的结果。这会增加更多本可以避免的计算频率,更重要的是,这样的比较脱离了实际业务,没有体现对应的映射关系。
在提一个更好的方法之前,我先来顿理论分析。
search(
[(days, operator, value), ]
)
=>
search(
[]
).filtered(
lambda s: operator_lambda(
s.days, timedelta(days=value)
)
)
=>
search(
[]
).filtered(
lambda s: operator_lambda(
date.today() - s.create_date, timedelta(days=value)
)
)
=>
search(
[]
).filtered(
lambda s: operator_lambda(
date.today() - timedelta(days=value), s.create_date)
)
)
=>
search(
[]
).filtered(
lambda s: reverse_operator_lambda(
s.create_date, date.today() - timedelta(days=value)
)
)
=>
search(
[('create_date', reverse_operator, date.today() - timedelta(days=value)), ]
)
私以为上面的domain推导某种程度某些方面某个时刻是成立的。
def _search_days(cls, operator, value):
target_date = date.today() - timedelta(days=value)
operator_mapping = {
'>': '<',
'>=': '<=',
'=': '=',
'<': '>',
'<=': '>=',
}
return [('create_day', operator_mapping[operator], target_date)]
注意到了吗?我甚至返回的也是一个 不精确 的domain条件,但这样才好。
为什么?因为当我给你[('state','=','done')]这样的条件时,你不会纠结到底是哪几张单,你也不会去担心这个问题,因为你大概率觉得这出这些id本就是数据库的工作。可不是嘛?
那又为什么要对这个非储存的计算型字段高看一眼,替数据库完成它的工作呢?
也许这就是中间层的意义吧。不要打扰它原先的运行机制,包括风格在内。
顺便一提,为什么要用一个不存在的create_day而不是create_date来做解释呢?理论上时间类型也支持timedelta的加减,也可以向前向后推算的不是吗?
简而言之,通常我们可以认为date类型属于一种00:00:00的datetime。正因我如此确定所有单据都以这样定死的后缀结尾,当我们去随机访问时,我也可以直接利用date.today()构建一个0点0分0秒的当天来比对timedelta.days,而不用考虑“尾差”所带来的timedelta.seconds对days的干扰。然而,当我们以datetime的视角去要求N天(前/时/后)时,以12:00:00为例,我们可以通过domain大致找到那些介于0.5天至1.5天之间的单据。至于“尾差”部分如何判断嘛?来人,上filtered。