Odoo ORM API (一)- Recordsets、Environment、Common ORM methods

Recordsets

從8.0開始提供一種新式API,接下來也會長期支持這個新式的API。在本片中,也是介紹怎麼在新舊API之間切換,但是舊API介紹的不多,如果有需要,請查看老版文檔。
與 models 和 records 的交互都是通過一個特有的東西 recordsets 來執行的,它是一個根據id 已經排好序的 同 models 的 record 的集合

注意:
雖然名字是 recordsets,是一個集合,但是仍然有較小的概率導致裏面包含重複的 record,這個將在未來被修復。

在models中定義的 方法, 都是作用於 recordset中,self 就代表 recordset

class AModel(models.Model):
    _name = 'a.model'

    def a_method(self):
        # self 可以是 數據庫中 0 或者所有當前model record 的集合
        self.do_operation()

通過迭代 self,將 yield 一個 新的長度爲1的 recordset,這個和普通的python string一樣,迭代一個string,yield 一個長度爲1的新的 string

def do_opertion(self):
    print self  # a.model(1, 2, 3, 4, 5)
    for record in self:
        print record   # => a.model(1,) then a.model(2,) .... a.model(5,)

Field access

recordsets 提供一個接口:models的各個field 可以直接被 record 通過 ‘.’來讀和寫,但是這個recordset的長度必須是 1,通過這種方式設置 field的值時,將會調用的一個database的update

>>> record.name
Examle name
>>> record.company_id.name
Company name
>>> record.name="Bob"

如果在 recordset長度不等於 1的情況下,訪問或者修改field的值,將會拋出一個異常
通過這種方式,獲取關係型field的值(Many2one, One2many, Many2many),將會返回一個 recordsets,如果沒有設置這些的字段的值得話,讀取這些字段,將會返回這個字段對應model的空recordset。

重要:
每一次對field進行賦值,都將會涉及一次database的update操作,所以,當你在某個時間,需要對多個字段的值進行修改,請使用write()

# 3 * len(records) 的數據庫操作
for record in records:
    record.a = 1
    record.b = 2
    record.c = 3

# len(records)次 數據庫操作
for record in records:
    record.write({'a':1, 'b':2, 'c':3})

# 1 次數據庫操作
records.write({'a':1,'b':2','c':3})

Set operations

Recordsets 是不可變的,但是同一model下面的不同recordset可以通過一定的操作符進行運算,並且返回一個新的recordsets,

  • record in set 將返回 這個record(必須是長度爲1的recordsets)是否在set 中。not in 就是相反的意思
  • set1 <= set2 以及 set1 < set2 將返回 set1 是否是 set2 的一個子集。
  • set1 >= set2 以及 set1 > set2 將返回 set1 是否完全包含 set2。
  • set1 | set2 是將兩個 set 合併起來,返回一個 不包含重複record的新的recordsets
  • set1 & set2 返回他們的交集
  • set1 - set2 返回 Set1 中所有不在 set2 中的 recordsets

Other recordset operations

Recordsets 是可迭代的,所以大部分Python的方法都可以作用於它(比如:map,filter,sorted…),但是這些方法返回的是一個 list 而不是一個 iterator,這將導致返回的數據,不能使用方法,以及set的各種運算。
因此,我們提供一個下面的幾個方法,使得返回的數據任然是recordsets

  • filtered()
    返回一個recordsets,這個recordsets中的所有record都滿足filter提供的函數,或者在filter中傳入一個string,裏面是record的某個字段
# only keep records whose company is the current user'
records.filtered(lambda r: r.company_id == user.company_id)

# only keep records whose partner is a company
records.filtered('partner_id.is_company')
  • sorted()
    返回一個根據提供參數作爲排序依據的 recordsets
# returns a list of summing two fields for each record in the set
records.sorted(lambda r: r.field1 + r.field2)
  • mapped()
    將作爲參數提供的function作用於每一個record上,如果返回的值是一個recordsets就返回一個recordsets,否則返回一個list
# 返回一個由各個record的兩個字段之和組成的list
records.mapped(lambda r: r.field1 + r.field2)

也可以傳入string用以獲取字段值:

# returns a list of names
records.mapped('name')

# returns a recordset of partners
record.mapped('partner_id')

# returns the union of all partner banks, with duplicates removed
record.mapped('partner_id.bank_ids')

Environment

Environment 儲存了各種各樣的 contextual data: 數據庫連接的 cursor(用以操作數據庫),當前登錄的user(用以檢測各項權限),當前的context(storing arbitrary metadata),同樣也儲存 caches。
所有的recordsets都有一個 不可變的environment,可以通過 env 來獲取 usercrcontext 等。

>>> records.env
<Environment object ...>
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...>

當從一個recordsets創建另一個recordset時,environment會被繼承過去。recordset通過environment可以創建一個新的**空的**recordset,並且通過新的recordsets來獲取對應model的數據:

>>> self.env['res.partner']
res.partner
>>> self.env['res.partner'].search([['is_company', '=', True], ['customer', '=', True]])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)

Altering the environment

通過recordset可以提供一個新的environment,並且返回一個根據新的environment下的recordset

  • sudo()
    通過提供的 user recordsets 創建一個新的 environment,如果不提供參數的話,將默認使用administrator作爲參數,返回一個根據新的environment作用下的recordset
# 以administrator的權利,創建一個新的 res.partner
env['res.partner'].sudo().create({'name': "A Partner"})

# 顯示所有 public 用戶能看到的 res.partner
public = env.ref('base.public_user')
env['res.partner'].sudo(public).search([])
  • with_context()

    1. 可以傳入一個位置參數,用以替換當前environment下的context
    2. 可以傳入一堆位置參數,更新當前的environment的context
# look for partner, or create one with specified timezone if none is found
env['res.partner'].with_context(tz=a_tz).find_or_create(email_address)
  • with_env()
    替換當前的environment

Common ORM methods

  • search()
    接受一個 search domain,返回一個滿足條件的recordset,也可以通過設置 offset and limi 參數來控制返回子集,通過設置order 來設置排序條件
>>> # searches the current model
>>> self.search([('is_company', '=', True), ('customer', '=', True)])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
>>> self.search([('is_company', '=', True)], limit=1).name
'Agrolait'

提示:
如果說,只想要查詢滿足條件的record有多少,請使用 search_count()

  • create()
    接受一些鍵值對,返回一個根據上面參數創建的新的recordset
>>> self.create({'name': "New Name"})
res.partner(78)
  • write()
    接受一些鍵值對,並修改recordset中所有record對應key的值,不返回值

  • browse()
    接受一個database id 或者 一個由 id組成的 list,返回對應model的 id等於提供參數的 recordset

>>> self.browse([7, 18, 12])
res.partner(7, 18, 12)
  • exists()
    返回一個在數據庫中存在的recordsets,通常用於檢驗某個record是否還在數據庫中
if not record.exists():
    raise Exception('The record has been deleted')

或者在執行某種刪除操作後,再處理一下

records.may_remove_some()
# 保留那些沒有被刪除的record
records = records.exists()
  • ref()
    Environment method,用以根據 external id 返回一個record
>>> env.ref('base.group_public')
res.groups(2)
  • ensure_one()
    檢查這個recordset是否長度爲1,如果不是,將會拋出異常
records.ensure_one()
assert len(records) == 1, "Excepted singleton"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章