創建模型
在模型上字段作爲屬性被定義
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 – 如果用戶對某些給定記錄沒有讀權限