odoo基礎(ORM API)

一、Recordsets(記錄集)

與模型和記錄的交互是通過記錄集執(zhí)行的,記錄集是同一模型的有序記錄集。模型上定義的方法在記錄集上執(zhí)行,它們的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()

迭代記錄集將產(chǎn)生新的單個記錄集(“單例”),就像迭代Python字符串產(chǎn)生單個字符的字符串一樣:

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), ...
1.字段訪問

記錄集提供“活動記錄”界面:模型字段可以作為屬性直接從記錄中讀取和寫入,但僅限于單個記錄(單記錄記錄集)。字段值也可以像dict項一樣訪問,這比動態(tài)字段名稱的getattr()更優(yōu)雅,更安全。設置字段的值會觸發(fā)對數(shù)據(jù)庫的更新:

>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob
2.記錄集的緩存和預取

Odoo為記錄字段維護一個緩存,因此并非每個字段訪問都會發(fā)出數(shù)據(jù)庫請求,這對性能來說太糟糕了。以下示例僅針對第一個語句查詢數(shù)據(jù)庫:

record.name             # first access reads value from database
record.name             # second access gets value from cache

為了避免一次在一條記錄上讀取一個字段,Odoo會根據(jù)一些啟發(fā)式方法預取記錄和字段以獲得良好的性能。一旦必須在給定記錄上讀取字段,ORM實際上會在較大的記錄集上讀取該字段,并將返回的值存儲在緩存中供以后使用。預取記錄集通常是記錄集,記錄來自迭代。
Moreover, all simple stored fields (boolean, integer, float, char, text, date, datetime, selection, many2one) are fetched altogether; they correspond to the columns of the model's table, and are fetched efficiently in the same query.
中文(簡體)
此外,所有簡單的存儲字段(布爾,整數(shù),浮點數(shù),字符,文本,日期,日期時間,選擇,許多2)都被完全取出;它們對應于模型表的列,并在同一查詢中有效獲取。請考慮以下示例,其中partners是1000條記錄的記錄集。如果沒有預取,循環(huán)將對數(shù)據(jù)庫進行2000次查詢。通過預取,只進行一個查詢:

for partner in partners:
    print partner.name          # first pass prefetches 'name' and 'lang'
                                # (and other fields) on all 'partners'
    print partner.lang

預取也適用于輔助記錄:當讀取關系字段時,它們的值(即記錄)將被訂閱以供將來預取,訪問其中一個輔助記錄會預取同一模型中的所有輔助記錄。這使得以下示例僅生成兩個查詢,一個用于合作伙伴,另一個用于國家/地區(qū):

countries = set()
for partner in partners:
    country = partner.country_id        # first pass prefetches all partners
    countries.add(country.name)     
3.集合操作

Recordsets are immutable, but sets of the same model can be combined using various set operations, returning new recordsets. Set operations do not preserve order.
中文(簡體)
記錄集是不可變的,但可以使用各種set操作組合相同模型的集合,返回新的記錄集。設置操作不保留順序。
① set中的record返回set中是否存在record(必須是1元素記錄集)。記錄不在set中是反向操作。
② set1 <= set2和set1 <set2返回set1是否為set2的子集(分別為strict)
③ set1> = set2和set1> set2返回set1是超集還是set2(分別嚴格)
④ set1 | set2返回兩個記錄集的并集,一個包含任一源中存在的所有記錄的新記錄集
⑤ set1和set2返回兩個記錄集的交集,一個新的記錄集只包含兩個源中的記錄
⑥ set1 - set2返回一個新記錄集,其中只包含set1中不在set2中的記錄

4.其他記錄集的操作

記錄集是可迭代的,因此常用的Python工具可用于轉(zhuǎn)換(map(),sorted(),ifilter(),...)但是這些工具返回列表或迭代器,刪除了在結(jié)果上調(diào)用方法的能力,或者使用集合操作。因此,記錄集提供這些操作返回記錄集本身(如果可能):
filtered()
返回僅包含滿足提供的謂詞函數(shù)的記錄的記錄集。謂詞也可以是一個字符串,按字段為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()
返回按提供的鍵函數(shù)排序的記錄集。如果未提供密鑰,請使用模型的默認排序順序:

# sort records by name
records.sorted(key=lambda r: r.name)

mapped()
將提供的函數(shù)應用于記錄集中的每個記錄,如果結(jié)果是記錄集,則返回記錄集:

# returns a list of summing two fields for each record in the set
records.mapped(lambda r: r.field1 + r.field2)

提供的函數(shù)可以是獲取字段值的字符串:

# 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存儲ORM使用的各種上下文數(shù)據(jù):數(shù)據(jù)庫游標(用于數(shù)據(jù)庫查詢),當前用戶(用于訪問權限檢查)和當前上下文(存儲任意元數(shù)據(jù))。environment還存儲緩存。所有記錄集都有一個不可變的environment,可以使用env訪問,并允許訪問當前用戶(用戶),游標(cr)或上下文(context):

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

從其他記錄集創(chuàng)建記錄集時,將繼承environment。該environment可用于在其他模型中獲取空記錄集,并查詢該模型:

>>> 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)
修改environment

可以從記錄集中自定義environment。這將使用更改的environment返回記錄集的新版本。
sudo()
使用提供的用戶集創(chuàng)建新environment,如果未提供任何用戶,則使用管理員(繞過安全上下文中的訪問權限/規(guī)則),使用新environment返回調(diào)用它的記錄集的副本:

# create partner object as administrator
env['res.partner'].sudo().create({'name': "A Partner"})
# list partners visible by the "public" user
public = env.ref('base.public_user')
env['res.partner'].sudo(public).search([])

with_context()
1.可以采用單個位置參數(shù),它替換當前環(huán)境的上下文
2.可以通過關鍵字獲取任意數(shù)量的參數(shù),這些參數(shù)將添加到當前environment的上下文或步驟1中設置的上下文中

# 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()
完全取代現(xiàn)有environment

三、常見ORM操作

search()
采用搜索域,返回匹配記錄的記錄集??梢苑祷仄ヅ溆涗浀淖蛹ㄆ坪拖拗茀?shù))并進行排序(順序參數(shù)):

>>> # 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'

create()
獲取許多字段值,并返回包含創(chuàng)建的記錄的記錄集:

>>> self.create({'name': "New Name"})
res.partner(78)

write()
獲取許多字段值,將它們寫入其記錄集中的所有記錄。不返回任何東西:

self.write({'name': "Newer Name"})

browse()
獲取數(shù)據(jù)庫ID或id列表并返回記錄集,當從外部Odoo獲取記錄ID(例如,通過外部系統(tǒng)往返)或在舊API中調(diào)用方法時非常有用:

>>> self.browse([7, 18, 12])
res.partner(7, 18, 12)

exists()
返回僅包含數(shù)據(jù)庫中存在的記錄的新記錄集??捎糜跈z查記錄(例如從外部獲得)是否仍然存在:

if not record.exists():
    raise Exception("The record has been deleted")

或者在調(diào)用可能刪除了一些記錄的方法之后:

records.may_remove_some()
# only keep records which were not deleted
records = records.exists()

⑥r(nóng)ef()
environment方法返回與提供的外部id匹配的記錄:

>>> env.ref('base.group_public')
res.groups(2)

ensure_one()
檢查記錄集是否為單例(僅包含單個記錄),否則會引發(fā)錯誤:

records.ensure_one()
# is equivalent to but clearer than:
assert len(records) == 1, "Expected singleton"

四、創(chuàng)建Models

模型字段定義為模型本身的屬性:

from odoo import models, fields
class AModel(models.Model):
    _name = 'a.model.name'

    field1 = fields.Char()

字段定義常用參數(shù)

1.string

默認情況下,字段的標簽(用戶可見名稱)是字段名稱的大寫版本,可以使用字符串參數(shù)覆蓋:

field2 = fields.Integer(string="an other field")

有關各種字段類型和參數(shù),請參閱字段參考。

2. default

默認值被定義為字段上的參數(shù),值為:

a_field = fields.Char(default="a value")

或者一個被調(diào)用來計算默認值的函數(shù),它應該返回該值:

def compute_default_value(self):
    return self.get_value()
a_field = fields.Char(default=compute_default_value)
3.computed fields

可以使用compute參數(shù)計算字段(而不是直接從數(shù)據(jù)庫中讀取)。它必須將計算值分配給字段。如果它使用其他字段的值,則應使用depends()指定這些字段:

from odoo import api
total = fields.Float(compute='_compute_total')

@api.depends('value', 'tax')
def _compute_total(self):
    for record in self:
        record.total = record.value + record.value * record.tax

使用子字段時,依賴關系可以是點出來的路徑:

@api.depends('line_ids.value')
def _compute_total(self):
    for record in self:
        record.total = sum(line.value for line in record.line_ids)

默認情況下不存儲計算字段,計算它們并在請求時返回。設置store = True會將它們存儲在數(shù)據(jù)庫中并自動啟用搜索
也可以通過設置搜索參數(shù)來啟用在計算字段上搜索。該值是返回域的方法名稱:

upper_name = field.Char(compute='_compute_upper', search='_search_upper')

def _search_upper(self, operator, value):
    if operator == 'like':
        operator = 'ilike'
    return [('name', operator, value)]

要允許在計算字段上設置值,請使用inverse參數(shù)。它是反轉(zhuǎn)計算和設置相關字段的函數(shù)的名稱:

document = fields.Char(compute='_get_document', inverse='_set_document')

def _get_document(self):
    for record in self:
        with open(record.get_document_path) as f:
            record.document = f.read()
def _set_document(self):
    for record in self:
        if not record.document: continue
        with open(record.get_document_path()) as f:
            f.write(record.document)

可以通過相同的方法同時計算多個字段,只需在所有字段上使用相同的方法并設置所有字段:

discount_value = fields.Float(compute='_apply_discount')
total = fields.Float(compute='_apply_discount')

@depends('value', 'discount')
def _apply_discount(self):
    for record in self:
        # compute actual discount from discount percentage
        discount = record.value * record.discount
        record.discount_value = discount
        record.total = record.value - discount
4. related fields

計算字段的特殊情況是相關(代理)字段,其提供當前記錄上的子字段的值。它們是通過設置相關參數(shù)來定義的,就像它們可以存儲的常規(guī)計算字段一樣:

nickname = fields.Char(related='user_id.partner_id.name', store=True)
5. onchange:動態(tài)更新UI

當用戶更改表單中的字段值(但尚未保存表單)時,根據(jù)該值自動更新其他字段可能很有用。更改稅額或添加新發(fā)票行時更新最終總計
計算字段會自動檢查并重新計算,它們不需要更改。
對于非計算字段,onchange()裝飾器用于提供新的字段值:

@api.onchange('field1', 'field2') # if these fields are changed, call method
def check_change(self):
    if self.field1 < self.field2:
        self.field3 = True
6.Low-level SQL

環(huán)境中的cr屬性是當前數(shù)據(jù)庫事務的游標,并允許直接執(zhí)行SQL,對于難以使用ORM表達的查詢(例如復雜連接)或出于性能原因:

self.env.cr.execute("some_sql", param1, param2, param3)

由于模型使用相同的游標,而環(huán)境包含各種緩存,因此在原始SQL中更改數(shù)據(jù)庫時,這些緩存必須無效,否則模型的進一步使用可能會變得不連貫。
在SQL中使用CREATE,UPDATE或DELETE時必須清除緩存,而不是SELECT(它只是讀取數(shù)據(jù)庫)。

五、Compatibility between new API and old API(新API和舊API之間的兼容性)

Odoo目前正在從較舊(較不常規(guī))的API轉(zhuǎn)換,可能需要手動從一個API手動橋接到另一個:
1.RPC層(XML-RPC和JSON-RPC)都是用舊API表示的,純粹在新API中表示的方法不能通過RPC獲得
2.可以從仍舊用舊API樣式編寫的舊代碼片段調(diào)用可覆蓋的方法
舊API和新API之間的巨大差異是:
1.environment的值(游標,用戶ID和上下文)顯式傳遞給方法
2.記錄數(shù)據(jù)(ID)顯式傳遞給方法,可能根本不傳遞
3.方法往往適用于ID列表而不是記錄集
默認情況下,假定方法使用新的API樣式,并且不能從舊的API樣式調(diào)用。
tips:
從新API到舊API的調(diào)用被橋接 使用新的API樣式時,使用舊API定義的方法的調(diào)用會自動轉(zhuǎn)換,不需要做任何特殊的事情:

>>> # method in the old API style
>>> def old_method(self, cr, uid, ids, context=None):
...    print ids

>>> # method in the new API style
>>> def new_method(self):
...     # system automatically infers how to call the old-style
...     # method from the new-style method
...     self.old_method()

>>> env[model].browse([1, 2, 3, 4]).new_method()
[1, 2, 3, 4]

兩個裝飾器可以向舊API公開新式方法:

1.model()

該方法暴露為不使用id,其記錄集通常為空。它的“舊API”簽名是:cr, uid, *arguments, context:

@api.model
def some_method(self, a_value):
    pass
# can be called as
old_style_model.some_method(cr, uid, a_value, context=context)
2.multi()

這個方法暴露為一個id列表(可能是空的),它的“舊API”簽名是:cr, uid, ids, *arguments, context:

@api.multi
def some_method(self, a_value):
    pass
# can be called as
old_style_model.some_method(cr, uid, [id1, id2], a_value, context=context)

因為新式API傾向于返回記錄集,而舊式API傾向于返回id列表,所以還有一個裝飾器管理這個:

3.returns()

假定函數(shù)返回一個記錄集,第一個參數(shù)應該是記錄集模型或self的名稱(對于當前模型)。如果在新API樣式中調(diào)用該方法,但在從舊API樣式調(diào)用時將記錄集轉(zhuǎn)換為id列表,則無效:

>>> @api.multi
... @api.returns('self')
... def some_method(self):
...     return self
>>> new_style_model = env['a.model'].browse(1, 2, 3)
>>> new_style_model.some_method()
a.model(1, 2, 3)
>>> old_style_model = pool['a.model']
>>> old_style_model.some_method(cr, uid, [1, 2, 3], context=context)
[1, 2, 3]
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 記錄集 model的數(shù)據(jù)是通過數(shù)據(jù)集合的形式來使用的,定義在model里的函數(shù)執(zhí)行時它們的self變量也是一個數(shù)據(jù)...
    XiaoHaiYang閱讀 17,115評論 0 16
  • 〇、前言 本文共108張圖,流量黨請慎重! 歷時1個半月,我把自己學習Python基礎知識的框架詳細梳理了一遍。 ...
    Raxxie閱讀 19,589評論 17 410
  • 記錄集(Recordsets) 本文涉及的API是基于Odoo 8.0及之后版本的,7.0版本之前的API稱為“舊...
    luohuayong閱讀 4,728評論 1 6
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • 使用xcode可以進行和git進行簡單的版本控制。首先我們需要安裝git環(huán)境,這些網(wǎng)上有很多文章進行了總結(jié),過程都...
    loghm閱讀 1,495評論 0 0

友情鏈接更多精彩內(nèi)容