如何使用odoo的compute方法,自動計算odoo欄位

FANDX發表於2021-07-27

前言

在odoo的ORM建立資料欄位的過程中,我們會經常需要定義一些欄位用來計算某一些欄位只和或其他計算結果。

今天介紹一個很好用的方法compute計算屬性,這個方法其實是屬於寫在odoo fields中的屬性,但是因為非常常用,還涉及ORM中的方法所以今天就單獨列出來詳細講解它的用法。

如何使用odoo compute屬性實現自動計算欄位

我們看下面的案例。

class FandxProduct(models.Model):
    _name = "fandx.product"

    name = fields.Char("產品名稱")
    nums = fields.Integer("數量")
    unit_price = fields.Float("產品單位價格")

現在有個需求,這是一個產品表,我們需要計算他的每一個產品的總價格(產品單位價格*數量),這時候就要用到我們的主角compute屬性,下面看案例。

class FandxProduct(models.Model):
    _name = "fandx.product"

    name = fields.Char("產品名稱")
    nums = fields.Integer("數量")
    unit_price = fields.Float("產品單位價格")
    # 使用_compute_all_price
    all_price = fields.Float("產品總價格", compute="_compute_all_price")

    def _compute_all_price(self):
        # self取值預設是multi,所以需要迴圈拿到每一個record的值
        for record in self:
            # 計算每一個record的all_price的值進行賦值
            record.all_price = record.unit_price * record.nums

在欄位中定義屬性compute並指向計算方法,在資料建立的時候會自動計算這個欄位的資料。

這樣我們在XML中使用all_price就可以獲取到產品的總價格,但是這裡還有一個問題。

資料庫中是沒有進行持久儲存的,那麼資料資料量一大,每次渲染頁面都要進行計算,這樣是非常消耗伺服器效能的,所以我們就要將計算的欄位給儲存起來。

如何儲存odoo compute計算欄位的值

方法很簡單,只需要在定義的field中加上store欄位,這樣就會把計算到的結果給儲存到資料庫中。

class FandxProduct(models.Model):
    _name = "fandx.product"

    name = fields.Char("產品名稱")
    nums = fields.Integer("數量")
    unit_price = fields.Float("產品單位價格")
    # 使用_compute_all_price
    # 在欄位中加上store = True實現資料的持久化
    all_price = fields.Float("產品總價格", compute="_compute_all_price", store=True)

    def _compute_all_price(self):
        # self取值預設是multi,所以需要迴圈拿到每一個record的值
        for record in self:
            # 計算每一個record的all_price的值進行賦值
            record.all_price = record.unit_price * record.nums

這樣我們在資料庫中就有了all_price的欄位來記錄產品總價格。

但是現在又出現一個問題,計算欄位是計算出了我們的結果,但是如果nums改變,或者unit_price改變了,計算欄位並不會改變怎麼辦!

compute配合使用depends監聽資料變化

class FandxProduct(models.Model):
    _name = "fandx.product"

    name = fields.Char("產品名稱")
    nums = fields.Integer("數量")
    unit_price = fields.Float("產品單位價格")
    # 使用_compute_all_price
    # 在欄位中加上store = True實現資料的持久化
    all_price = fields.Float("產品總價格", compute="_compute_all_price", store=True)

    # 將nums、unit_price欄位進行監聽,在資料變動的時候再次執行compute指向的方法進行重新賦值計算。
    @api.depends('nums', 'unit_price')
    def _compute_all_price(self):
        # self取值預設是multi,所以需要迴圈拿到每一個record的值
        for record in self:
            # 計算每一個record的all_price的值進行賦值
            record.all_price = record.unit_price * record.nums

到這裡就基本實現了compute的所有經常使用的方法,下面一般會配合compute一起使用的屬性inverse

odoo中逆向計算inverse屬性使用詳解

上面講解了compute方法是用來計算對應的資料欄位的。

inverse方法其實就是compute的逆向方法,預設情況下xml中對應的compute計算欄位是readonly的。

當我們加上inverse逆向計算的時候,那麼就可以在XML中輸入對應的值,然後系統會走inverse對應的方法。

class FandxProduct(models.Model):
    _name = "fandx.product"

    name = fields.Char("產品名稱")
    nums = fields.Integer("數量")
    unit_price = fields.Float("產品單位價格")
    # 使用_compute_all_price
    # 在欄位中加上store = True實現資料的持久化
    all_price = fields.Float("產品總價格", compute="_compute_all_price", inverse='_set_unit_price', store=True)

    # 將nums、unit_price欄位進行監聽,在資料變動的時候再次執行compute指向的方法進行重新賦值計算。
    @api.depends('nums', 'unit_price')
    def _compute_all_price(self):
        # self取值預設是multi,所以需要迴圈拿到每一個record的值
        for record in self:
            # 計算每一個record的all_price的值進行賦值
            record.all_price = record.unit_price * record.nums

    def _set_unit_price(self):
        for record in self:
            if not all([record.nums, record.all_price]):
                continue
            
            # 當我們手動修改all_price的值的時候,我們就可以逆向去計算出unit_price的值,進行重新賦值。
            record.unit_price = record.all_price / record.nums

總結

  1. odoo的compute可以實現我們對資料欄位自動計算的需求。
  2. fields中加入store屬性可以實現資料的持久化儲存。
  3. compute計算欄位在持久化儲存之後不會在自動計算,需要我們配合depends來監聽指定計算欄位在資料改動的時候重新計算資料欄位。
  4. inverse方法可以幫助我們解決compute計算欄位readonly的問題,並在輸入對應的欄位進行逆向計算之前的欄位。
  5. compute欄位預設是store為False的所有search是無效的,還可以指定search的方式實現搜尋。這裡就不去細講了,很簡單也不常用。
  6. 還有compute_sudo屬性可以設定欄位在使用的時候是否以超級管理員的方式來進行計算,這裡store=True的時候預設為True,否則預設為False,瞭解一下就可以了。
  7. 如果有問題可以在下方留言。

相關文章