0O00--計算型字段的搜索方法
就拿一個比較常見的需求舉例吧。如果我想跟進某個單據(jù)的進度,想篩選出距其創(chuàng)建起1天內(nèi),2天內(nèi),7天內(nèi)的所有單據(jù),怎么辦?
懶得廢話了。
方案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',)
因為系統(tǒng)無法感知到每一天的時間變化,所以就就形成了這樣一個非儲存的計算型的字段。
好吧,其實這個字段建立之初就只是為了用于篩選,我們就去看看非儲存的計算型字段怎么被應用于搜索視圖吧。
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)]
為這樣非儲存的字段構(gòu)建了一個支持比較的搜索方法,是odoo為我們提供的標準機制。
但問題是,這樣的方法對嗎?好嗎?跟我們該干的事吻合嗎?
回顧一下search屬性的本意吧。
已知現(xiàn)在在業(yè)務層需要對days做篩選,而數(shù)據(jù)庫并不理解這個表的days是什么東西,從而需要odoo后端在中間做過渡,或許轉(zhuǎn)義更貼切一些。
重新再來看看這個search方法,大概率是可行的,走的通的,不夠好的。
因為[('id','in',selfs.ids)]這樣的返回值,早就脫離了轉(zhuǎn)義的范疇。
domain之所以被設計成3元元組的列表結(jié)構(gòu),就是為了體現(xiàn)其中每一個條件的業(yè)務含義,不然倒不如直接接收若干個ids的合集,用來取交集并集補集不是更直接一些?
具體來看,這個搜索方法并不在乎業(yè)務,理論上可以適用于任何字段的搜索需求,完全是依賴其compute方法執(zhí)行后的結(jié)果。這會增加更多本可以避免的計算頻率,更重要的是,這樣的比較脫離了實際業(yè)務,沒有體現(xiàn)對應的映射關(guān)系。
在提一個更好的方法之前,我先來頓理論分析。
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')]這樣的條件時,你不會糾結(jié)到底是哪幾張單,你也不會去擔心這個問題,因為你大概率覺得這出這些id本就是數(shù)據(jù)庫的工作??刹皇锹??
那又為什么要對這個非儲存的計算型字段高看一眼,替數(shù)據(jù)庫完成它的工作呢?
也許這就是中間層的意義吧。不要打擾它原先的運行機制,包括風格在內(nèi)。?
順便一提,為什么要用一個不存在的create_day而不是create_date來做解釋呢?理論上時間類型也支持timedelta的加減,也可以向前向后推算的不是嗎?
簡而言之,通常我們可以認為date類型屬于一種00:00:00的datetime。正因我如此確定所有單據(jù)都以這樣定死的后綴結(jié)尾,當我們?nèi)ルS機訪問時,我也可以直接利用date.today()構(gòu)建一個0點0分0秒的當天來比對timedelta.days,而不用考慮“尾差”所帶來的timedelta.seconds對days的干擾。然而,當我們以datetime的視角去要求N天(前/時/后)時,以12:00:00為例,我們可以通過domain大致找到那些介于0.5天至1.5天之間的單據(jù)。至于“尾差”部分如何判斷嘛?來人,上filtered。