ORM API(二)

創建模型

在模型上字段作爲屬性被定義

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

    field1 = fields.Char()

警告
意味着字段和方法不能有相同的名字,否則會衝突

默認情況下,顯示給用戶的是首字母大寫的字段名,但是可以被參數"string"覆蓋

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

字段的默認值通過參數定義,如下

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

或者調用一個函數,獲取函數的返回值

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

計算字段

字段的值可以通過compute參數來計算,代替直接從數據庫中獲取。必須將計算的值賦給字段,如果需要用到其它字段時,應該用depends()修飾符

from openerp 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"可以存儲到數據庫中並且啓用自動搜索

搜索參數也可以被計算來啓用計算字段上的搜索,這個方法返回一個domains

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"參數可以在一個字段上設置值,它的值是一個函數名,反轉計算並設置相關字段。

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

related字段

計算字段的特殊字段是related字段,它提供當前記錄上的子字段的值。它們是通過設置相關參數來定義的,像常規的計算字段一樣,它們可以存儲:

nickname = fields.Char(related='user_id.partner_id.name', store=True)

onchange:動態更新UI

當用戶更新了字段的值(但並沒有保存),它能夠自動的更新其它字段的值

  • 計算字段能夠自動的檢查和重新計算,不需要onchange
  • 對於非計算字段,onchang()修飾符可以提供新的字段值
	# 如果這些字段值發生改變,調用check_change方法
	@api.onchange('field1', 'field2') 
	def check_change(self):
	    if self.field1 < self.field2:
	        self.field3 = True

方法執行過程中的更改對用戶可見

  • 客戶端自動調用計算字段和onchange()修飾符,不需要在視圖中添加
  • 可以在視圖中添加on_change="0"在特定字段中抑制觸發器。
   <field name="name" on_change="0"/>

當用戶編輯時,即使字段擁有onchange屬性,也不會觸發更新。

Note
onchange只是對記錄上的值進行計算,然後返回客戶端,並不會保存到數據庫中

Low-level SQL

環境上的cr屬性是當前數據庫事務的指針,允許直接執行SQL,對於用ORM難以表示的查詢或者性能原因,則可以使用sql:

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

由於模型使用相同的遊標,並且環境中保存着各種緩存,所以當使用sql修改數據庫時,這些緩存必須失效,否則模型的進一步使用可能會變得不連貫。在sql中使用創建、更新或者刪除時,有必要清楚緩存,而不是SELECT(它只是讀取數據庫)

新舊API之間的兼容

odoo正在從一個老的(不太常規的)API過渡,需要手動連接到另一個API:

  • RPC層(XML-RPC和JSON-RPC)是用舊API表示的,純在新API中表示的方法在RPC上不可用
  • 可重寫的方法可以從仍然使用舊API樣式編寫的舊代碼片段中調用

新舊API之間最大的區別是:

  • 環境的值(遊標、用戶id和上下文)被顯式地傳遞給方法
  • 記錄數據(ids)顯式地傳遞給方法,可能根本就不傳遞
  • 記錄數據(ids)顯式地傳遞給方法,可能根本就不傳遞

默認情況下,假定方法使用新的API樣式,並且不能從舊的API樣式調用

Tip
從新API到舊API的調用被橋接
當使用新的API樣式時,對使用舊API定義的方法的調用將自動動態轉換,不需要做任何特別的事情

	>>> # 舊API定義的方法
	>>> def old_method(self, cr, uid, ids, context=None):
	...    print ids

	>>> # 新API定義的方法
	>>> def new_method(self):
	...     # 系統自動推斷如何調用舊的樣式
	...     self.old_method()

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

兩個decorator可以向舊API公開一個新風格的方法:

model()
該方法公開爲不使用ids,其記錄集通常爲空。它的“舊API”簽名是cr、uid、*arguments、context:

@api.model
def some_method(self, a_value):
    pass
# 等於
old_style_model.some_method(cr, uid, a_value, context=context)

multi()
該方法公開爲一個id列表(可能爲空),其“舊API”簽名爲cr、uid、ids、*arguments、context:

@api.multi
def some_method(self, a_value):
    pass
# 等於
old_style_model.some_method(cr, uid, [id1, id2], a_value, context=context)

因爲新風格的api傾向於返回記錄集,而舊風格的api傾向於返回id列表,所以還有一個裝飾器來管理這一點:

returns()
假設函數返回一個記錄集,第一個參數應該是記錄集的模型或self(對於當前模型)的名稱。
如果以新的API樣式調用該方法,但從舊的API樣式調用時將記錄集轉換爲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]

Model Reference

class openerp.models.Model(pool, cr)

常規數據庫的主要超類——持久化OpenERP模型。
OpenERP模型是從這個類繼承而來的:

class user(Model):
    ...

系統稍後將在每個數據庫(安裝了類模塊的數據庫)上實例化該類一次。

結構屬性

_name
業務對象名稱,用點符號表示(在模塊名稱空間中)

_rec_name
用作名稱的替代字段,由osv的name_get()(默認:‘name’)使用

_inherit

  • 如果設置了_name,則繼承該模型以_name命名,並在其上進行擴展
  • 如果不設置_name,則繼承該模型,在原模型上進行擴展

_order
在沒有指定排序的情況下進行搜索時的排序字段(默認值:‘id’)

_auto
是否應該創建數據庫表(默認:True)如果設置爲False,則重寫init()以創建數據庫表

_table
是否應該創建數據庫表(默認:True)如果設置爲False,則重寫init()以創建數據庫表

_inherits
字典將父業務對象的_name映射到要使用的相應外鍵字段的名稱

_inherits = {
    'a.model': 'a_field_id',
    'b.model': 'b_field_id'
}

實現基於複合的繼承:新模型公開了_inherits-ed模型的所有字段,但沒有存儲任何字段:值本身仍然存儲在鏈接的記錄上。

_constraints
定義python約束的列表(constraint_function, message, fields),字段列表是指示性的。

_sql_constraints
在生成備份表時,定義SQL約束以執行的元組(name,sql_definition,message)

_parent_store
在parent_left和parent_right旁邊,設置一個嵌套集,以支持對當前模型記錄進行快速分層查詢(默認:False)

CRUD

create(vals)->record
在模型中創建一個新的記錄
新記錄使用vals中和default_get()中的值初始化

Parameters: vals(dict) –
模型字段的值,如字典

{'field_name': field_value, ...}

Returns:新創建的記錄
Raises: AccessError –

  • 如果用戶對請求的對象沒有創建權限
  • 如果用戶試圖繞過在請求對象上創建的訪問規則
  • ValidateError——如果用戶試圖爲未選擇的字段輸入無效值
  • UserError——如果在對象的層次結構中創建了一個循環,則是操作的結果(例如將對象設置爲其父對象)

browse([ids]) → records
返回當前環境中作爲參數提供的id的記錄集。
值可以爲空,單個id或者一個列表

unlink()
刪除當前集合的記錄

Raises: AccessError –

  • 如果用戶在請求的對象上沒有unlink權限
  • 如果用戶試圖繞過請求對象上取消鏈接的訪問規則
  • UserError——如果記錄是其他記錄的默認屬性

write(vals)
使用提供的值更新當前集合中的所有記錄。

Parameters: vals(dict) –
要更新的字段和要設置的值,例如:

{'foo': 1, 'bar': "Qux"}

將字段foo設爲1,字段bar設爲“Qux”(否則會觸發錯誤)
Raises: AccessError –

  • 如果用戶對請求的對象沒有寫權限
  • 如果用戶試圖繞過對請求對象進行寫操作的訪問規則
  • ValidateError——如果用戶試圖爲未選擇的字段輸入無效值
  • UserError——如果在對象的層次結構中創建了一個循環,則是操作的結果(例>- 如將對象設置爲其父對象)
  • 對於數值字段(整數、浮點數),值應該是相應的類型
  • 對於布爾值,值應該是bool
  • 對於選擇,該值應該匹配選擇值(通常爲str,有時爲int)
  • 對於Many2one,該值應該是要設置的記錄的數據庫標識符
  • 其它非關係字段使用字符串作爲值

Danger
由於歷史和兼容性的原因,日期和日期時間字段使用字符串作爲值(寫入和讀> 取),而不是日期或日期時間。這些日期字符串是utc格式的,並且根據openerp.tools.misc.DEFAULT_SERVER_DATE_FORMAT和openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT進行格式化。

  • One2many和Many2many使用一種特殊的“命令”格式來操作存儲在/與字段關聯的記錄集。

這種格式是順序執行的三元組列表,其中每個三元組是要在記錄集上執行的命令。並不是所有命令都適用於所有情況。可能的命令是:

(0, _, values)
添加從提供的值字典創建的新記錄。

(1, id, values)
使用值中的值更新現有的id id記錄。無法在create()中使用。

(2, id, _)
從集合中刪除id id記錄,然後(從數據庫中)刪除它。無法在create()中使用。

(3, id, _)
從集合中刪除id id記錄,但不刪除它。不能在One2many上使用。無法在create()中使用。

(4, id, _)
將現有的id id記錄添加到集合中。不能在One2many上使用。

(5, _, _)
從集合中刪除所有記錄,相當於顯式地對每個記錄使用命令3。不能在One2many上使用。無法在create()中使用。

(6, _, ids)
替換ids列表中設置的所有現有記錄,相當於對ids中的每個id使用命令5和命令4。不能在One2many上使用。

Note
上面列表中標記爲_的值被忽略,可以是任何值,通常爲0或False。

read([fields])
在self、low-level/RPC方法中爲記錄讀取請求字段。在Python代碼中,首選browse()。

Parameters: fields – 要返回的字段名稱列表(默認爲所有字段)
Returns: 將字段名稱映射到其值的字典列表,每個記錄有一個字典
Raises: AccessError – 如果用戶對某些給定記錄沒有讀權限

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