欢迎光临散文网 会员登陆 & 注册

0O00--计算型字段的搜索方法

2023-09-15 01:48 作者:你知道阿基米德原理吗  | 我要投稿

就拿一个比较常见的需求举例吧。如果我想跟进某个单据的进度,想筛选出距其创建起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。


0O00--计算型字段的搜索方法的评论 (共 条)

分享到微博请遵守国家法律