Odoo ORM API(四)- Methond decorators

Method decorators

這一小節介紹了對兩種不同風格的API的管理,這兩種API分別是 traditionalrecord,在traditional 風格中,一些參數(cr, uid, ids, context)是被顯示的設置在函數定義中,而在record 風格的API中,定義函數是不需要主動設置這些參數的,這些參數都會被隱藏在 model instances 中傳入,顯得更加的 object-oriented。
比如:

model = self.pool.get(MODEL)
ids = model.search(cr, uid, DOMAIN, context=context)
for rec in model.browse(cr, uid, ids, context=context):
    print rec.name
model.write(cr, uid, ids, VALUES, context=context)

也可以這樣

env = Environment(cr, uid, context) # cr, uid, context wrapped in env
model = env[MODEL]                  # retrieve an instance of MODEL
recs = model.search(DOMAIN)         # search returns a recordset
for rec in recs:                    # iterate over the records
    print rec.name
recs.write(VALUES)                  # update all records in recs

traditional 方式的method會被自動的decorated,可以跟着下面的列子,看看效果

  • openerp.api.multi(method)對一個 self 是 recordset的進行裝飾,需要按照下述方法
@api.multi
def method(self, args):
    ...

這樣就可以通過兩種方式對其調用

# recs = model.browse(cr, uid, ids, context)
recs.method(args)

model.method(cr, uid, ids, args, context=context)
  • openerp.api.model(method)
    對一個 self 是一個recordset的method進行裝飾,但是這個method與self中內容無太大關係,反而是與這個model有關聯,就用這種方式進行裝飾
@api.model
def method(self, args):
    ...

可以通過下面兩種方式調用

# recs = model.browse(cr, uid, ids, context)
recs.method(args)

model.method(cr, uid, args, context=context)

注意:這種裝飾之後,用traditional的方式調用,將不用傳入ids。

  • openerp.api.depends(*args)
    返回一個裝飾器,指定了某個compute field 依賴的fields(就相當於新API中的 function field),每一個參數必須是字符串,字符串中可以用 dot將field name 鏈接起來
pname = fields.Char(compute='_compute_pname')

@api.one
@api.depends('partner_id.name', 'partner_id.is_company')
def _compute_pname(self):
    if self.partner_id.is_company:
        self.pname = (self.partner_id.name or "").upper()
    else:
        self.pname = self.partner_id.name

也可以傳一個function作爲參數,在這種情況下,將直接用 model 調用 這個function 來設置他們之間的依賴

  • openerp.api.constrains(*args)
    用以設置限制條件,每個參數必須是字段的名字
@api.one
@api.constrains('name', 'description')
def _check_description(self):
    if self.name == self.description:
        raise ValidationError("Fields name and description must be different")

在傳入參數字段的值改變時,將會自動調用這個方法,進行檢查。函數定義中,如果不滿足條件,開發者應當raise ValidationError

警告:
constrians 僅僅支持普通的field name,dotted name 字段中的字段,比如 partner_id.customer 。這種將會被忽略,不會檢查。

  • openerp.api.onchange(*args)
    用以設置當某個字段變化時的效果,每一個參數必須是字段名字
@api.onchange('partner_id')
def _onchange_partner(self):
    self.message = "Dear %s" % (self.partner_id.name or "")

在這個field出現的 form view 中,如果這個field的值發生改變,那麼這個方法就會被調用。但是這個方法是作用在 form view 中的 值組成的一個 假的record 上面,方法中對這個record的改變也不是真正的改變,他僅僅是計算好值,並且將這個值發送給 client而已,用來改變form view 中對應的值得展現。
這個方法也可以返回一個字典,用以改變field domains 或者彈出一個警告窗口。

return {
    'domain': {'other_id': [('partner_id', '=', partner_id)]},
    'warning': {'title': "Warning", 'message': "What is this?"},
}

警告:
onchange 和 constrains 一樣,只能作用於普通的field name,關係型字段下的字段 (e.g: partner_id.tz),將會被忽略,並且不會生效。

  • openerp.api.returns(model, downgrade=None, upgrade=None)
    裝飾返回 instances of mode 的方法
Name Description
Parameters: model - 一個model的名字,或者self表示當前model
downgrade - 一個function downgrade(self, value, *args, **kwargs)用以將 record-style 的返回值 轉換成 traditional-style的值
upgrade - 一個function upgrade(self, value, *args, **kwargs)用以將 traditional-style 的返回值 轉換成 record-style的值

這兩個function中的 self,*args, **kwargs 都是record-style中的參數。
這個裝飾器使得函數的返回值根據調用者的style返回對應的值:
old-style:返回 id, ids, Flase
record-style:返回 recordset

@model
@returns('res.partner')
def find_partner(self, arg):
    ...     # return some record

# output depends on call style: traditional vs record style
partner_id = model.find_partner(cr, uid, arg, context=context)

# recs = model.browse(cr, uid, ids, context)
partner_record = recs.find_partner(arg)

Note that the decorated method must satisfy that convention.
Those decorators are automatically inherited: a method that overrides a decorated existing method will be decorated with the same @returns(model).

  • openerp.api.one(method)
    對一個 self 是 recordset的,進行裝飾。但是這個self應該是長度爲1。這個被裝飾後的method被調用時,將會自動一個loop 到調用它的每一個record上,並將每一個record的返回值組成一個列表返回。比如這個method同樣被 returns() 裝飾了,那麼他將會把結果一個的組合起來,再返回。
@api.one
def method(self, args):
    return self.name

也可以這樣調用

# recs = model.browse(cr, uid, ids, context)
names = recs.method(args)

names = model.method(cr, uid, ids, args, context=context)

只能在9.0及以後使用,one() 雖然可以使代碼簡單明瞭,但是它的行爲可能不會像開發者所預期的那樣
所以,我們強烈推薦使用multi(),在其中對recordset 進行迭代。如果明確是隻想作用於一個record上,可以在method中,使用 self.ensure_one(),然後就不需要使用迭代了。

  • openerp.api.v7(mothod_v7)
    對一個method裝飾,使其只支持 old-api style。一個 new-api style可以通過定義一個名字相同的method,但是用 v8()裝飾
@api.v7
def foo(self, cr, uid, ids, context=None):
    ...

@api.v8
def foo(self):
    ...

Special care must be taken if one method calls the other one, because the method may be overridden! In that case, one should call the method from the current class (say MyClass), for instance:

@api.v7
def foo(self, cr, uid, ids, context=None):
    # Beware: records.foo() may call an overriding of foo()
    records = self.browse(cr, uid, ids, context)
    return MyClass.foo(records)

Note that the wrapper method uses the docstring of the first method.

  • openerp.api.v8(method_v8)
    Decorate a method that supports the new-style api only. An old-style api may be provided by redefining a method with the same name and decorated with v7():
@api.v8
def foo(self):
    ...

@api.v7
def foo(self, cr, uid, ids, context=None):
    ...

Note that the wrapper method uses the docstring of the first method.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章