Odoo12 ORM API ☞ Recordsets
Recordsets(記錄集)
8.0版中的新功能: 此頁面記錄了Odoo 8.0中新增的 NEW API,它應該是未來的主要開發API。Odoo12依舊提供相容OpenERP 7.0更甚之前中"old API",但此處不再提及"old API",如需請參閱之前API文件。
與模型和記錄的互動是通過Recordsets執行的,Recordsets是同一模型的有序記錄集。
Warning!
Recordsets,名稱中為set,即為無重複記錄集,但目前Recordsets(記錄集)可能包含重複項。後邊可能會有所改變。
模型中定義的方法在記錄集上執行,方法的 self
class AModel(models.Model):
_name = 'a.model'
def a_method(self):
# self can be anywhere between 0 records and all records in the
# database
self.do_operation()
迭代記錄集將得到新的單個記錄(“singletons”),就像迭代Python字串得到單個字元:
def do_operation(self): print self # => a.model(1, 2, 3, 4, 5) for record in self: print record # => a.model(1), then a.model(2), then a.model(3), ...
Field access(欄位訪問)
Recordsets提供“Active Record” 介面:模型欄位可以作為屬性直接從記錄中讀取和寫入,但僅限於單例(single-record recordsets)。欄位的值就可以像字典一樣直接訪問,這種方式要比使用欄位的*“getattr()”*方法來訪問要優雅和安全。當設定欄位的值時會觸發資料庫的更新操作。
>>> record.name Example Name >>> record.company_id.name Company Name >>> record.name = "Bob" >>> field = "name" >>> record[field] Bob
當試圖對多記錄(multiple records)的進行讀取或者寫入操作時會報錯!
訪問一個關係欄位(如Many2one,One2many,Many2many)會始終返回記錄集,如果未設定欄位,則為空。
Danger!
對欄位的每個賦值都會觸發資料庫更新,在同時設定多個欄位或在多個記錄上設定欄位(相同的值)時,請使用write():
# 3 * len(records) database updates
for record in records:
record.a = 1
record.b = 2
record.c = 3
# len(records) database updates
for record in records:
record.write({'a': 1, 'b': 2, 'c': 3})
# 1 database update
records.write({'a': 1, 'b': 2, 'c': 3})
Record cache and prefetching(記錄快取和預取)
Odoo會為記錄欄位維護一個快取,這樣就不需要訪問每個欄位都發出資料庫請求,如果每取一個欄位就去資料庫查詢一次,效能就會變的很糟糕。示例中僅針對第一個語句查詢資料庫,第二次會從快取中取值:
record.name # first access reads value from database
record.name # second access gets value from cache
為了避免一次讀取一條記錄上一個欄位,Odoo按照一些啟發式方法預取記錄和欄位以獲得良好的效能。一旦必須在給定記錄上讀取欄位,ORM實際上會在在更大的記錄集上讀取該欄位,並將返回的值儲存在快取中供以後使用。預取記錄集通常是來自迭代形成的記錄集。此外,所有簡單的儲存欄位(boolean, integer, float, char, text, date, datetime, selection, many2one)都被完全取出;它們對應於模型表的列,並有效的在一次查詢中獲取。
考慮以下示例,其中partners是有1000條記錄的記錄集。如果沒有預取,就需要對資料庫進行迴圈2000次查詢。通過預取,只需要一個查詢:
for partner in partners:
print partner.name # first pass prefetches 'name' and 'lang'
# (and other fields) on all 'partners'
print partner.lang
Set operations(集合操作)
Recordsets 是不可變的,但可以使用各種set操作組合相同模型的集合,返回新的Recordsets 。設定操作不保留順序。
- record in set 返回 record 是否存在於 set 中。record not in set 即判斷是否不在 set 中。
- set1 <= set2 和set1 < set2 返回 set1 是否是 set2 的一個子集(或嚴格是一個子集)。
- set1 >= set2 和set1 > set2 返回 set1 是否是 set2 的一個父(超)集(或嚴格是一個父集)。
- set1 | set2 返回兩個recordsets的並集,包含任一 set 中都存在的所有記錄的新記錄集。
- set1 & set2 返回兩個recordsets的交集,包含兩個 set 中都存在的所有記錄的新記錄集。
- set1 - set2 返回僅包含 set1 的記錄的新記錄集,這些記錄不存在於 set2 中。
Other recordset operations(其他集合操作)
Recordsets 是可迭代的,因此通常的Python工具可用於轉換(map(),sorted(),itertools.ifilter,…)但是這些工具返回 list 或 iterator, 去掉了在返回結果上呼叫方法或使用集合操作的能力。
因此,Recordsets 提供這些操作返回recordsets (記錄集)本身的操作方法(如果可能):
filtered()
返回僅包含滿足提供的條件函式的記錄的記錄集。條件也可以是一個字串按欄位為true或false進行過濾:
# only keep records whose company is the current user's
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()
返回按提供的key函式排序的記錄集。如果未提供key,將使用模型的預設排序順序:
# sort records by name
records.sorted(key=lambda r: r.name)
mapped()
將提供的函式應用於Recordset中的每個record(記錄),如果結果是Recordset,則返回Recordset:
# returns a list of summing two fields for each record in the set
records.mapped(lambda r: r.field1 + r.field2)
提供的函式可以是獲取欄位值的字串:
# 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')