ORM API?
- 對象關系映射模塊:
分層結構
約束一致性和驗證
對象元數據取決于其狀態
通過復雜查詢進行優化處理(一次執行多個操作)
默認字段值
權限優化
持久化對象:DB postgresql
數據轉換
多級緩存系統
兩種不同的繼承機制
- 豐富的字段類型集合:
傳統類型(varchar、integer、boolean等)
關系型(一對多,多對一,多對多)
功能性的
模型?
模型字段定義為模型本身的屬性:
from odoo import models, fields
class AModel(models.Model):
_name = 'a.model.name'
field1 = fields.Char()
警告
這意味著您不能定義一個與方法同名的字段,否則后者將會悄悄地覆蓋前者。
默認情況下,字段的標簽(用戶可見名稱)是字段名稱的大寫版本,可以使用“string”參數覆蓋此設置。:
field2 = fields.Integer(string="Field Label")
有關字段類型和參數的列表,請參見: 字段參考 。
默認值是作為字段參數定義的,可以是一個值:
name = fields.Char(default="a value")
或者作為一個被調用以計算默認值的函數,該函數應該返回該值:
def _default_name(self):
return self.get_value()
name = fields.Char(default=lambda self: self._default_name())
API
- class odoo.models.BaseModel[源代碼]?
Odoo模型的基類。
Odoo模型是通過繼承以下模型之一創建的:
Model
用于常規數據庫持久化模型TransientModel
用于存儲在數據庫中的臨時數據,但會定期自動清理AbstractModel
用于抽象超類,可被多個繼承模型共享
系統會自動為每個數據庫實例化每個模型一次。這些實例代表每個數據庫上可用的模型,并且取決于該數據庫上安裝的模塊。每個實例的實際類是由創建和繼承相應模型的Python類構建的。
每個模型實例都是一個“記錄集”,即該模型的記錄的有序集合。記錄集由諸如
browse()
、search()
或字段訪問等方法返回。記錄沒有顯式的表示方式:一個記錄被表示為一個只包含一個記錄的記錄集。要創建一個不應該被實例化的類,可以將
_register
屬性設置為 False。- _auto = False?
是否應該創建數據庫表。如果設置為
False
,請覆蓋init()
方法來創建數據庫表。對于
Model
和TransientModel
,自動默認為True
,對于AbstractModel
,自動默認為False
。小技巧
要創建一個沒有表的模型,請繼承自
AbstractModel
。
- _sql_constraints = []?
SQL 約束條件 [(名稱,SQL定義,消息)]
- _register = False?
注冊表可見性
- _abstract = True?
模型是否為 抽象 模型。
另請參閱
- _transient = False?
模型是否為 瞬態 。
另請參閱
- _name = None?
模型名稱(點符號表示法,模塊命名空間)
- _description = None?
模型的非正式名稱
- _inherit = ()?
Python繼承的模型:
- 類型
字符串或字符串列表
- _inherits = {}?
字典{‘parent_model’: ‘m2o_field’}將父業務對象的_name映射到相應的外鍵字段名稱以使用:
_inherits = { 'a.model': 'a_field_id', 'b.model': 'b_field_id' }
實現基于組合的繼承:新模型公開繼承模型的所有字段,但不存儲它們:值本身仍存儲在鏈接記錄上。
警告
如果在
_inherits
中繼承的模型中定義了多個同名字段,那么繼承的字段將對應于最后一個(按照繼承列表的順序)。
- _rec_name = None?
用于標記記錄的字段,默認為“名稱”
- _order = 'id'?
搜索結果的默認排序字段
- _check_company_auto = False?
在寫入和創建時,調用
_check_company
來確保具有check_company=True
屬性的關系字段的公司一致性。
- _parent_name = 'parent_id'?
用作父級字段的many2one字段
- _parent_store = False?
設置為True以計算parent_path字段。
除了
parent_path
字段外,還設置了一個索引存儲記錄的樹形結構,以便使用child_of
和parent_of
域運算符更快地進行當前模型記錄的分層查詢。
- _fold_name = 'fold'?
用于確定看板視圖中折疊組的字段
抽象模型?
- odoo.models.AbstractModel[源代碼]?
alias of
odoo.models.BaseModel
模型?
- class odoo.models.Model(env, ids, prefetch_ids)[源代碼]?
常規數據庫持久化Odoo模型的主超類。
Odoo模型是通過繼承此類創建的:
class user(Model): ...
系統稍后將在每個安裝了該類模塊的數據庫中實例化該類。
- _auto = True?
是否應該創建數據庫表。如果設置為
False
,請覆蓋init()
方法來創建數據庫表。對于
Model
和TransientModel
,自動默認為True
,對于AbstractModel
,自動默認為False
。小技巧
要創建一個沒有表的模型,請繼承自
AbstractModel
。
- _abstract = False?
模型是否為 抽象 模型。
另請參閱
瞬態模型?
- class odoo.models.TransientModel(env, ids, prefetch_ids)[源代碼]?
瞬態記錄的模型超類,用于臨時持久化,并定期進行清理。
瞬態模型具有簡化的訪問權限管理,所有用戶都可以創建新記錄,并且只能訪問他們創建的記錄。超級用戶可以無限制地訪問所有瞬態模型記錄。
- _transient_max_count = False?
瞬態記錄的最大數量,如果為
0
則無限制
- _transient_max_hours = 1.0?
最大空閑生命周期(以小時為單位),如果為
0
則無限制
- _transient_vacuum()[源代碼]?
清理瞬態記錄。
每當達到
_transient_max_count
或_transient_max_hours
(如果有)條件時,此功能將從瞬態模型表中取消鏈接舊記錄。實際清理只會在每5分鐘內發生一次。這意味著可以頻繁調用此方法(例如每次創建新記錄時)。
同時啟用max_hours和max_count的示例:
假設max_hours = 0.2(即12分鐘),max_count = 20,表中有55行,其中有10行在最近5分鐘內創建/更改,另外12行在5到10分鐘之間創建/更改,其余的行創建/更改時間超過12分鐘。
基于時間的清理將保留最近12分鐘內創建/更改的22行記錄
計數基礎的清理將會清除另外12行。不僅僅是2行,否則每次添加都會立即導致最大值再次達到。
過去5分鐘內創建/更改的10行記錄將不會被刪除
字段?
- class odoo.fields.Field[源代碼]?
字段描述符包含字段定義,并管理記錄上相應字段的訪問和賦值。在實例化字段時,可以提供以下屬性:
- 參數
string (str) – 用戶可見的字段標簽;如果未設置,則ORM將使用類中的字段名稱(大寫)。
help (str) – 用戶可見的字段工具提示
invisible – 字段是否不可見(布爾型,默認為
False
)readonly (bool) – 字段是否只讀(默認值:
False
)。這僅影響用戶界面。任何代碼中的字段賦值都將起作用(如果字段是存儲字段或可逆字段)。required (bool) – 字段的值是否必填(默認值:
False
)index (str) – 字段是否在數據庫中建立索引,以及索引的類型。注意:這不會影響非存儲和虛擬字段??赡艿闹禐椋?*
"btree"
或True
:標準索引,適用于many2one *"btree_not_null"
:沒有NULL值的BTREE索引(當大多數值為NULL或從不搜索NULL時有用) *"trigram"
:具有三元組的廣義反向索引(適用于全文搜索)*None
或False
:沒有索引(默認)default (value or callable) – 字段的默認值;可以是靜態值,也可以是一個接收記錄集并返回值的函數;使用
default=None
來丟棄字段的默認值。states (dict) – 一個將狀態值映射到UI屬性值對列表的字典;可能的屬性有:
readonly
,required
,invisible
。..警告:任何基于狀態的條件都需要在客戶端UI上可用的state
字段值。這通常是通過在相關視圖中包含它來完成的,如果對最終用戶不相關,則可能會使其不可見。groups (str) – 逗號分隔的組xml id列表(字符串);這將限制字段訪問僅限于給定組的用戶
company_dependent (bool) – 字段值是否依賴于當前公司;該值不存儲在模型表中,而是注冊為
ir.property
。當需要使用company_dependent字段的值時,會搜索與當前公司(和當前記錄,如果存在一個屬性)相關聯的ir.property
。如果在記錄上更改了該值,則會修改當前記錄的現有屬性(如果存在一個),或為當前公司和res_id創建一個新屬性。如果在公司側更改了該值,則會影響所有未更改該值的記錄。copy (bool) – 是否在復制記錄時復制字段值(默認情況下,對于普通字段為
True
,對于one2many
和計算字段(包括屬性字段和相關字段)為False
)store (bool) – 字段是否存儲在數據庫中(default:
True
,計算字段為False
)group_operator (str) – 聚合函數用于
read_group()
在對該字段進行分組時。支持的聚合函數有: *array_agg
:將值(包括空值)連接成一個數組*count
:行數 *count_distinct
:不同行數*bool_and
:如果所有值都為真,則為真,否則為假 *bool_or
:如果至少有一個值為真,則為真,否則為假*max
:所有值中的最大值 *min
:所有值中的最小值*avg
:所有值的平均值(算術平均值)*sum
:所有值的總和group_expand (str) – 用于在當前字段分組時擴展read_group結果的函數。.. code-block:: python @api.model def _read_group_selection_field(self, values, domain, order): return [‘choice1’, ‘choice2’, …] # 可用的選擇項。 @api.model def _read_group_many2one_field(self, records, domain, order): return records + self.search([custom_domain])
計算字段
- 參數
precompute (bool) – 是否在記錄插入數據庫之前計算字段。當字段可以在記錄插入之前計算時,應該手動將某些字段指定為precompute=True。(例如,避免基于搜索/讀取組的統計字段,many2one鏈接到上一個記錄等)(默認值:
False
).. warning:: 僅當未提供顯式值和默認值時,預計算才會發生create()。這意味著默認值會禁用預計算,即使將字段指定為precompute=True也是如此。如果給定模型的記錄不是批量創建的,則預計算字段可能會適得其反??紤]一次創建許多記錄的情況。如果字段沒有預計算,則通常會在flush()中批量計算,并且預取機制將有助于使計算效率高。另一方面,如果字段已經預計算,則計算將逐個進行,因此將無法利用預取機制。根據上述備注,預計算字段可能在one2many的行上很有趣,這些行通常由ORM本身批量創建,前提是它們是通過寫入包含它們的記錄來創建的。compute_sudo (bool) – 字段是否應該作為超級用戶重新計算以繞過訪問權限(對于存儲字段默認為
True
,對于非存儲字段默認為False
)recursive (bool) – 字段是否具有遞歸依賴關系(字段
X
具有類似于parent_id.X
的依賴關系);聲明字段為遞歸必須是顯式的,以確保重新計算的正確性。inverse (str) – 反轉字段的方法名稱(可選)
search (str) – 實現字段搜索的方法名稱(可選)
related (str) – 字段名稱的順序
default_export_compatible (bool) – 字段是否必須在兼容導入的導出中默認導出 .. 參見:: 高級字段/相關字段
基本字段?
- class odoo.fields.Float[源代碼]?
封裝了一個
float
。精度數字由(可選的)
digits
屬性給出。當浮點數與計量單位相關聯時,使用正確的工具以正確的精度比較或舍入值非常重要。
Float類提供了一些靜態方法來實現此目的:
round()
用于將浮點數四舍五入到指定精度。is_zero()
用于檢查浮點數是否在指定精度下等于零。compare()
用于比較兩個浮點數在指定精度下的大小。示例
按照計量單位的精度四舍五入數量:
fields.Float.round(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
使用計量單位的精度檢查數量是否為零:
fields.Float.is_zero(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
比較兩個數量:
field.Float.compare(self.product_uom_qty, self.qty_done, precision_rounding=self.product_uom_id.rounding)
比較助手出于歷史目的使用 __cmp__ 語義,因此使用此助手的適當、慣用方式如下:
如果result == 0,則第一個和第二個浮點數相等;如果result < 0,則第一個浮點數小于第二個;如果result > 0,則第一個浮點數大于第二個。
高級字段?
- class odoo.fields.Binary[源代碼]?
封裝二進制內容(例如文件)。
- 參數
attachment (bool) – 字段是否應存儲為
ir_attachment
或模型表的列(默認值:True
)。
- class odoo.fields.Html[源代碼]?
封裝 HTML 代碼內容。
- 參數
sanitize (bool) – 值是否需要進行過濾(默認值:
True
)sanitize_overridable (bool) – 是否允許屬于
base.group_sanitize_override
用戶組的用戶繞過衛生處理(默認值:False
)sanitize_tags (bool) – 是否對標簽進行清理(只接受白名單屬性,默認為
True
)sanitize_attributes (bool) – 是否對屬性進行清理(只接受白名單中的屬性,默認值:
True
)sanitize_style (bool) – 是否清理樣式屬性(默認值:
False
)strip_style (bool) – 是否剝離樣式屬性(被移除并因此未經過消毒, 默認值:
False
)strip_classes (bool) – 是否剝離類屬性(默認值:
False
)
- class odoo.fields.Image[源代碼]?
封裝一個圖像,擴展
Binary
。如果圖像大小超過像素的
max_width
/max_height
限制,則圖像將按比例縮小到限制大小。- 參數
注解
如果未指定
max_width
/max_height
(或設置為0),并且verify_resolution
為False,則不會驗證字段內容,應使用Binary
字段。
- class odoo.fields.Selection[源代碼]?
封裝了不同值之間的互斥選擇。
- 參數
selection (list(tuple(str,str)) or callable or str) – 指定此字段的可能取值??梢允怯梢唤M
(value, label)
對組成的列表,也可以是一個模型方法或方法名。selection_add (list(tuple(str,str))) – 提供了在覆蓋字段的情況下選擇的擴展。它是一個由
(value, label)
或(value,)
成對或單個值的列表,其中單個值必須出現在被覆蓋的選擇中。新值按與被覆蓋選擇一致的順序插入到此列表中: selection = [(‘a’, ‘A’), (‘b’, ‘B’)] selection_add = [(‘c’, ‘C’), (‘b’,)] > result = [(‘a’, ‘A’), (‘c’, ‘C’), (‘b’, ‘B)’]ondelete – 提供了一個回退機制,用于任何帶有selection_add的覆蓋字段。它是一個字典,將selection_add中的每個選項映射到一個回退操作。這個回退操作將應用于所有selection_add選項映射到它的記錄。這些操作可以是以下任何一種: - ‘set null’ – 默認值,所有具有此選項的記錄將其選擇值設置為False。 - ‘cascade’ – 所有具有此選項的記錄將與該選項本身一起被刪除。 - ‘set default’ – 所有具有此選項的記錄將被設置為字段定義的默認值 - ‘set VALUE’ – 所有具有此選項的記錄將被設置為給定值 - <callable> – 一個可調用的函數,其第一個和唯一的參數將是包含指定Selection選項的記錄集,用于自定義處理
屬性
selection
是必需的,除非是related
或擴展字段的情況。
日期(時間)字段?
Dates
和 Datetimes
是任何業務應用程序中非常重要的字段。它們的錯誤使用可能會創建看不見但痛苦的錯誤,本節旨在為Odoo開發人員提供避免誤用這些字段所需的知識。
當給日期/時間字段賦值時,以下選項是有效的:
一個
date
或datetime
對象。一個符合服務器格式的字符串:
False
或None
。
Date和Datetime字段類有助手方法,嘗試轉換為兼容類型:
to_date()
將轉換為datetime.date
to_datetime()
將轉換為datetime.datetime
。
Example
解析來自外部來源的日期/日期時間::
fields.Date.to_date(self._context.get('date_from'))
日期/時間比較最佳實踐:
日期字段 只能 與日期對象進行比較。
Datetime字段 只能 與datetime對象進行比較。
警告
表示日期和日期時間的字符串可以相互比較,但結果可能不是預期的結果,因為日期時間字符串始終大于日期字符串,因此 強烈 不建議這種做法。
常見的日期和時間操作,如加減或獲取周期的開始/結束,都可以通過 Date
和 Datetime
進行操作。這些 助手函數也可以通過導入 odoo.tools.date_utils
來使用。
注解
時區
日期時間字段在數據庫中以 timestamp without timezone
列的形式存儲,并以UTC時區存儲。這是有意設計的,因為它使Odoo數據庫獨立于托管服務器系統的時區。時區轉換完全由客戶端管理。
- class odoo.fields.Date[源代碼]?
封裝了一個python
date
對象。- static add(value, *args, **kwargs)[源代碼]?
返回
value
和relativedelta
的和。- 參數
value – 初始日期或日期時間。
args – 直接傳遞給
relativedelta
的位置參數。kwargs – 直接傳遞給
relativedelta
的關鍵字參數。
- 返回
結果日期/日期時間。
- static context_today(record, timestamp=None)[源代碼]?
返回當前日期,以客戶端時區為準,格式適合日期字段。
注解
此方法可用于計算默認值。
- 參數
record – 獲取時區的記錄集。
timestamp (datetime) – 可選的日期時間值,用于代替當前日期和時間(必須是日期時間,普通日期無法在時區之間轉換)。
- 返回類型
date
- static end_of(value, granularity)[源代碼]?
從日期或日期時間獲取時間段的結束時間。
- 參數
value – 初始日期或日期時間。
granularity – 時間段類型字符串,可以是年、季度、月、周、日或小時。
- 返回
指定時間段開始的日期/日期時間對象。
- static start_of(value, granularity)[源代碼]?
從日期或日期時間獲取時間段的開始。
- 參數
value – 初始日期或日期時間。
granularity – 時間段的類型,可以是年、季度、月、周、日或小時。
- 返回
一個對應于指定時間段開始的日期/日期時間對象。
- static subtract(value, *args, **kwargs)[源代碼]?
返回
value
和relativedelta
之間的差異。- 參數
value – 初始日期或日期時間。
args – 直接傳遞給
relativedelta
的位置參數。kwargs – 直接傳遞給
relativedelta
的關鍵字參數。
- 返回
結果日期/日期時間。
- static to_date(value)[源代碼]?
嘗試將
value
轉換為date
對象。警告
如果傳入的值是一個日期時間對象,它將被轉換為日期對象,所有日期時間特定的信息將會丟失(如時分秒,時區等)。
- class odoo.fields.Datetime[源代碼]?
封裝了一個python
datetime
對象。- static add(value, *args, **kwargs)[源代碼]?
返回
value
和relativedelta
的和。- 參數
value – 初始日期或日期時間。
args – 直接傳遞給
relativedelta
的位置參數。kwargs – 直接傳遞給
relativedelta
的關鍵字參數。
- 返回
結果日期/日期時間。
- static context_timestamp(record, timestamp)[源代碼]?
返回給定的時間戳轉換為客戶端時區。
注解
該方法 不應 作為默認初始化器使用,因為日期時間字段在客戶端顯示時會自動轉換。對于默認值,應使用
now()
。- 參數
record – 獲取時區的記錄集。
timestamp (datetime) – 將表示為UTC的本地日期時間值轉換為客戶端時區。
- 返回
將時間戳轉換為上下文時區的帶時區感知的日期時間。
- 返回類型
datetime
- static end_of(value, granularity)[源代碼]?
從日期或日期時間獲取時間段的結束時間。
- 參數
value – 初始日期或日期時間。
granularity – 時間段類型字符串,可以是年、季度、月、周、日或小時。
- 返回
指定時間段開始的日期/日期時間對象。
- static start_of(value, granularity)[源代碼]?
從日期或日期時間獲取時間段的開始。
- 參數
value – 初始日期或日期時間。
granularity – 時間段的類型,可以是年、季度、月、周、日或小時。
- 返回
一個對應于指定時間段開始的日期/日期時間對象。
- static subtract(value, *args, **kwargs)[源代碼]?
返回
value
和relativedelta
之間的差異。- 參數
value – 初始日期或日期時間。
args – 直接傳遞給
relativedelta
的位置參數。kwargs – 直接傳遞給
relativedelta
的關鍵字參數。
- 返回
結果日期/日期時間。
關系型字段?
- class odoo.fields.Many2one[源代碼]?
這樣的字段的值是一個大小為0(沒有記錄)或1(單個記錄)的記錄集。
- 參數
comodel_name (str) – 目標模型的名稱
必填
,除了相關或擴展字段之外。domain – 在客戶端上設置在候選值上的可選域(域或字符串)
context (dict) – 在處理該字段時,在客戶端使用的可選上下文
ondelete (str) – 當所引用的記錄被刪除時應該采取的操作;可能的取值為:
'set null'
,'restrict'
,'cascade'
auto_join (bool) – 是否在通過該字段進行搜索時生成JOIN(默認值:
False
)delegate (bool) – 將其設置為
True
,使得目標模型的字段可以從當前模型訪問(對應于_inherits
)check_company (bool) – 在
_check_company()
中標記該字段進行驗證。根據字段屬性添加默認公司域。
- class odoo.fields.One2many[源代碼]?
One2many字段;此類字段的值是
comodel_name
中所有記錄的記錄集,其中字段inverse_name
等于當前記錄。- 參數
除了相關字段或字段擴展的情況外,
comodel_name
和inverse_name
屬性是必需的。
- class odoo.fields.Many2many[源代碼]?
Many2many字段;此字段的值為記錄集。
- 參數
屬性
relation
、column1
和column2
是可選的。如果沒有提供,名稱將從模型名稱自動生成,前提是model_name
和comodel_name
不同!請注意,在給定模型上具有多個具有隱式關系參數的字段,且具有相同的關聯模型是不被ORM接受的,因為這些字段將使用相同的表。ORM防止兩個many2many字段使用相同的關系參數,除非
兩個字段使用相同的模型、關聯模型和關系參數是明確的;或者
至少有一個字段屬于
_auto = False
的模型。
- 參數
domain – 在客戶端上設置在候選值上的可選域(域或字符串)
context (dict) – 在處理該字段時,在客戶端使用的可選上下文
check_company (bool) – 在
_check_company()
中標記該字段進行驗證。根據字段屬性添加默認公司域。
- class odoo.fields.Command[源代碼]?
One2many
和Many2many
字段需要特殊的命令來操作它們實現的關系。在內部,每個命令都是一個3元組,第一個元素是一個必需的整數,用于標識命令,第二個元素要么是相關記錄的ID,以應用命令(命令更新、刪除、取消鏈接和鏈接),要么是0(命令創建、清除和設置),第三個元素要么是要寫入記錄的“值”(命令創建和更新),要么是相關記錄的新“ids”列表(命令設置),要么是0(命令刪除、取消鏈接、鏈接和清除)。
通過Python,我們鼓勵開發人員通過此命名空間的各種函數來創建新命令。我們還鼓勵開發人員在比較現有命令的第一個元素時使用命令標識符常量名稱。
通過RPC,無法使用函數或命令常量名稱。必須改為編寫字面量3元組,其中第一個元素是命令的整數標識符。
- CREATE = 0?
- UPDATE = 1?
- DELETE = 2?
- UNLINK = 3?
- LINK = 4?
- CLEAR = 5?
- SET = 6?
- classmethod create(values: dict)[源代碼]?
使用“values”在關聯模型中創建新記錄,并將創建的記錄鏈接到“self”。
在
Many2many
關系中,會在 comodel 中創建一個唯一的新記錄,使得self
中的所有記錄都鏈接到新記錄。在
One2many
關系中,對于self
中的每個記錄,都會在關聯模型中創建一個新記錄,以便self
中的每個記錄都與新記錄中的一個記錄精確關聯。返回命令三元組
(CREATE, 0, values)
- classmethod delete(id: int)[源代碼]?
從數據庫中刪除相關記錄并刪除其與“self”的關系。
在
Many2many
關系中,如果記錄仍與其他記錄相關聯,則可能會阻止從數據庫中刪除該記錄。返回命令三元組
(DELETE, id, 0)
偽關系字段?
計算字段?
可以使用 compute
參數計算字段(而不是直接從數據庫中讀?。?。 必須將計算出的值分配給該字段 。如果它使用其他 字段 的值,則應使用 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
設置為真將會把它們存儲在數據庫中并自動啟用搜索。可以通過設置
search
參數來啟用計算字段的搜索功能。該值是一個返回 搜索域 的方法名稱。: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)]
在執行實際模型搜索之前,處理域時會調用搜索方法。它必須返回等效于條件的域:
field 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') @api.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
警告
雖然可以在多個字段中使用相同的計算方法,但不建議在反向方法中這樣做。
在計算反向關系時, 所有 使用該反向關系的字段都受到保護,這意味著即使它們的值不在緩存中,也無法計算它們的值。
如果訪問這些字段并且它們的值不在緩存中,ORM 將簡單地為這些字段返回默認值 False
。這意味著除了觸發反向方法的字段之外的反向字段的值可能不會給出它們的正確值,這可能會破壞反向方法的預期行為。
自動字段?
訪問日志字段?
如果啟用了 _log_access
,這些字段將自動設置和更新??梢越盟员苊庠趯λ鼈儧]有用處的表上創建或更新這些字段。
默認情況下, _log_access
的值與 _auto
相同。
警告
_log_access
必須 在 TransientModel
上啟用。
保留字段名稱?
一些字段名稱被保留用于預定義的行為,超出了自動化字段的范疇。當需要相關的行為時,應在模型上定義它們:
- Model.active?
切換記錄的全局可見性,如果
active
設置為False
,則該記錄在大多數搜索和列表中都是不可見的。特殊方法:
- Model.action_archive()[源代碼]?
調用當前活動記錄的
toggle_active()
方法,將記錄集的active
屬性設置為False
。
- Model.action_unarchive()[源代碼]?
通過調用當前未激活的記錄的
toggle_active()
方法,將記錄集的active
屬性設置為True
。
- Model.parent_id?
_parent_name
的默認值,用于在樹形結構中組織記錄,并在域中啟用child_of
和parent_of
運算符。
- Model.parent_path?
當
_parent_store
設置為 True 時,用于存儲反映_parent_name
樹形結構的值,并優化搜索域中的child_of
和parent_of
運算符。必須使用index=True
聲明以確保正常運行。
記錄集?
與模型和記錄的交互是通過記錄集進行的,記錄集是相同模型的記錄的有序集合。
警告
與其名稱所暗示的相反,目前記錄集中可能包含重復項。這在未來可能會改變。
在模型上定義的方法在記錄集上執行,它們的 self
是一個記錄集:
class AModel(models.Model):
_name = 'a.model'
def a_method(self):
# self can be anything between 0 records and all records in the
# database
self.do_operation()
迭代記錄集將產生新的 單個記錄 集合(”單例”),就像在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), ...
字段訪問?
記錄集提供了一個“Active Record”接口:模型字段可以直接作為屬性從記錄中讀取和寫入。
注解
當在可能包含多個記錄的記錄集上訪問非關系字段時,請使用 mapped()
total_qty = sum(self.mapped('qty'))
字段值也可以像字典項一樣訪問,這比使用動態字段名的 getattr()
更加優雅和安全。設置字段的值會觸發對數據庫的更新:
>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob
警告
嘗試讀取多個記錄上的字段將會對非關系型字段引發錯誤。
訪問關系字段( Many2one
, One2many
, Many2many
) 總是 返回一個記錄集,如果字段未設置則為空。
記錄緩存和預取?
Odoo 維護記錄字段的緩存,以便不是每個字段訪問都會發出數據庫請求,這對性能來說是可怕的。以下示例僅查詢第一個語句的數據庫:
record.name # first access reads value from database
record.name # second access gets value from cache
為了避免逐個讀取每個記錄的每個字段,Odoo 預取 記錄和字段,遵循一些啟發式規則以獲得良好的性能。一旦必須在給定記錄上讀取一個字段,ORM 實際上會在更大的記錄集上讀取該字段,并將返回的值存儲在緩存中以供以后使用。預取的記錄集通常是通過迭代從中獲取記錄的記錄集。此外,所有簡單存儲的字段(布爾值、整數、浮點數、字符、文本、日期、日期時間、選擇、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
預取數據也適用于 次要記錄 :當讀取關系字段時,它們的值(即記錄)將被訂閱以進行未來的預取。訪問其中一個次要記錄將預取來自同一模型的所有次要記錄。這使得以下示例僅生成兩個查詢,一個用于合作伙伴,一個用于國家::
countries = set()
for partner in partners:
country = partner.country_id # first pass prefetches all partners
countries.add(country.name) # first pass prefetches all countries
方法修飾器?
Odoo API 模塊定義了 Odoo 環境和方法修飾符。
- odoo.api.autovacuum(method)[源代碼]?
裝飾一個方法,使其被每日自動清理計劃任務(模型“ir.autovacuum”)調用。這通常用于類似垃圾回收的任務,不需要特定的計劃任務。
- odoo.api.constrains(*args)[源代碼]?
裝飾一個約束檢查器。
每個參數必須是在檢查中使用的字段名稱:
@api.constrains('name', 'description') def _check_description(self): for record in self: if record.name == record.description: raise ValidationError("Fields name and description must be different")
在已修改命名字段之一的記錄上調用。
如果驗證失敗,應該引發
ValidationError
異常。警告
@constrains
僅支持簡單字段名稱,不支持點分名稱(例如關系字段的字段partner_id.customer
),將被忽略。僅當裝飾方法中聲明的字段包含在
create
或write
調用中時,才會觸發@constrains
。這意味著在視圖中不存在的字段在記錄創建期間不會觸發調用。必須覆蓋create
以確保始終觸發約束(例如,測試值的缺失)。也可以將單個函數作為參數傳遞。在這種情況下,通過使用模型實例調用該函數來獲取字段名稱。
- odoo.api.depends(*args)[源代碼]?
返回一個裝飾器,指定“compute”方法(用于新式函數字段)的字段依賴關系。每個參數必須是由點分隔的字段名稱序列:
pname = fields.Char(compute='_compute_pname') @api.depends('partner_id.name', 'partner_id.is_company') def _compute_pname(self): for record in self: if record.partner_id.is_company: record.pname = (record.partner_id.name or "").upper() else: record.pname = record.partner_id.name
也可以將單個函數作為參數傳遞。在這種情況下,通過使用字段模型調用函數來確定依賴項。
- odoo.api.depends_context(*args)[源代碼]?
返回一個裝飾器,指定非存儲“compute”方法的上下文依賴項。每個參數都是上下文字典中的一個鍵:
price = fields.Float(compute='_compute_product_price') @api.depends_context('pricelist') def _compute_product_price(self): for product in self: if product.env.context.get('pricelist'): pricelist = self.env['product.pricelist'].browse(product.env.context['pricelist']) else: pricelist = self.env['product.pricelist'].get_default_pricelist() product.price = pricelist._get_products_price(product).get(product.id, 0.0)
所有依賴項必須是可哈希的。以下鍵具有特殊支持:
company
(上下文中的值或當前公司ID),uid
(當前用戶ID和超級用戶標志),active_test
(env.context 或 field.context 中的值)。
- odoo.api.model(method)[源代碼]?
裝飾一個記錄式方法,其中
self
是一個記錄集,但其內容不相關,只有模型是相關的。這樣一個方法:@api.model def method(self, args): ...
- odoo.api.model_create_multi(method)[源代碼]?
裝飾一個接受字典列表并創建多個記錄的方法。該方法可以使用單個字典或字典列表調用:
record = model.create(vals) records = model.create([vals, ...])
- odoo.api.onchange(*args)[源代碼]?
返回一個裝飾器,用于裝飾給定字段的 onchange 方法。
在包含該字段的表單視圖中,當給定字段之一被修改時,該方法將被調用。該方法在一個偽記錄上被調用,該記錄包含表單中存在的值。該記錄上的字段賦值會自動發送回客戶端。
每個參數必須是字段名稱:
@api.onchange('partner_id') def _onchange_partner(self): self.message = "Dear %s" % (self.partner_id.name or "")
return { 'warning': {'title': "Warning", 'message': "What is this?", 'type': 'notification'}, }
如果類型設置為通知,則警告將顯示在通知中。否則,默認情況下將在對話框中顯示。
警告
@onchange
僅支持簡單字段名稱,不支持點分隔的名稱(例如關系字段的字段,如partner_id.tz
),這些名稱將被忽略危險
由于
@onchange
返回偽記錄的記錄集,因此在上述記錄集上調用任何一個CRUD方法(create()
,read()
,write()
,unlink()
)都是未定義的行為,因為它們可能尚不存在于數據庫中。相反,只需像上面的示例一樣設置記錄的字段或調用
update()
方法即可。警告
無法通過onchange修改
one2many
或many2many
字段本身。這是Web客戶端的限制 - 請參見 #2693。
- odoo.api.ondelete(*, at_uninstall)[源代碼]?
標記一個方法,在
unlink()
執行期間被執行。這個裝飾器的目的是允許在取消關聯記錄時出現客戶端錯誤,如果從業務角度來看,刪除這些記錄是沒有意義的。例如,用戶不應該能夠刪除已驗證的銷售訂單。
雖然可以通過簡單地覆蓋模型上的
unlink
方法來實現,但它的缺點是與模塊卸載不兼容。在卸載模塊時,覆蓋可能會引發用戶錯誤,但我們不應該關心,因為模塊正在被卸載,因此與模塊相關的 所有 記錄都應該被刪除。這意味著通過覆蓋
unlink
,有很大的機會使一些表/記錄成為未卸載模塊的剩余數據。這將使數據庫處于不一致的狀態。此外,如果在該數據庫上重新安裝該模塊,則存在沖突的風險。使用
@ondelete
裝飾的方法應在某些條件下引發錯誤,按照慣例,該方法的名稱應為_unlink_if_<condition>
或_unlink_except_<not_condition>
。@api.ondelete(at_uninstall=False) def _unlink_if_user_inactive(self): if any(user.active for user in self): raise UserError("Can't delete an active user!") # same as above but with _unlink_except_* as method name @api.ondelete(at_uninstall=False) def _unlink_except_active_user(self): if any(user.active for user in self): raise UserError("Can't delete an active user!")
- 參數
at_uninstall (bool) – 裝飾方法是否應在實現該方法的模塊被卸載時調用。幾乎總是應該為“False”,以避免模塊卸載觸發這些錯誤。
危險
如果您實現的檢查也適用于卸載模塊,請將參數
at_uninstall
僅設置為True
。例如,卸載“銷售”模塊時,如果已驗證的銷售訂單被刪除,這并不重要,因為與“銷售”相關的所有數據都應該被刪除,此時應將“at_uninstall”設置為“False”。
然而,如果沒有安裝其他語言,防止刪除默認語言是有意義的,因為刪除默認語言將破壞很多基本行為。在這種情況下,
at_uninstall
應該設置為True
。
- odoo.api.returns(model, downgrade=None, upgrade=None)[源代碼]?
返回一個裝飾器,用于返回“model”實例的方法。
- 參數
model – 一個模型名稱,或者
'self'
表示當前模型downgrade – 一個函數
downgrade(self, value, *args, ** kwargs)
用于將記錄樣式的value
轉換為傳統樣式的輸出upgrade – 一個函數
upgrade(self, value, *args, ** kwargs)
,用于將傳統風格的value
轉換為記錄風格的輸出
參數
self
、 `` args`` 和 ``* kwargs`` 是以記錄方式傳遞給方法的參數。該裝飾器將方法輸出適應于API風格:傳統風格的
id
、ids
或False
,以及記錄風格的記錄集:@model @returns('res.partner') def find_partner(self, arg): ... # return some record # output depends on call style: traditional vs record style partner_id = model.find_partner(cr, uid, arg, context=context) # recs = model.browse(cr, uid, ids, context) partner_record = recs.find_partner(arg)
請注意,被裝飾的方法必須符合這個約定。
這些裝飾器會自動 繼承 :覆蓋已裝飾方法的方法將使用相同的
@returns(model)
進行裝飾。
環境?
- class odoo.api.Environment(cr, uid, context, su=False)[源代碼]?
環境存儲ORM使用的各種上下文數據:
cr
:當前數據庫游標(用于數據庫查詢);uid
:當前用戶ID(用于訪問權限檢查);context
:當前上下文字典(任意元數據);su
: 是否處于超級用戶模式。
它通過實現從模型名稱到模型的映射來提供對注冊表的訪問。它還持有記錄的緩存和管理重新計算的數據結構。
>>> records.env
<Environment object ...>
>>> records.env.uid
3
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...>
在從另一個記錄集創建記錄集時,環境會被繼承。環境可用于在另一個模型中獲取空記錄集,并查詢該模型:
>>> 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.user?
返回當前用戶(作為實例)。
- 返回
當前用戶 - 已使用sudo命令
- 返回類型
res.users 記錄
- Environment.company?
返回當前公司(作為實例)。
如果在上下文中未指定(
allowed_company_ids
),則回退到當前用戶的主公司。- 引發
AccessError – 無效或未授權的
allowed_company_ids
上下文鍵內容。- 返回
當前公司(默認值=
self.user.company_id
),與當前環境一起- 返回類型
res.company 記錄
警告
在sudo模式下不適用任何健全性檢查!在sudo模式下,用戶可以訪問任何公司,即使不在他允許的公司列表中。
這允許觸發跨公司修改,即使當前用戶沒有訪問目標公司的權限。
- Environment.companies?
返回用戶啟用的公司的記錄集。
如果上下文中沒有指定(
allowed_company_ids
),則回退到當前用戶的公司。- 引發
AccessError – 無效或未授權的
allowed_company_ids
上下文鍵內容。- 返回
當前公司(默認為
self.user.company_ids
),與當前環境一起- 返回類型
res.company記錄集
警告
在sudo模式下不適用任何健全性檢查!在sudo模式下,用戶可以訪問任何公司,即使不在他允許的公司列表中。
這允許觸發跨公司修改,即使當前用戶沒有訪問目標公司的權限。
有用的環境方法?
- Environment.ref(xml_id, raise_if_not_found=True)[源代碼]?
返回與給定的
xml_id
相對應的記錄。- 參數
- 返回
找到記錄或無記錄
- 引發
ValueError – 如果記錄未找到且
raise_if_not_found
為 True
修改環境?
- Model.with_context([context][, **overrides]) Model [源代碼]?
返回一個新版本的記錄集,附加到擴展上下文。
擴展上下文是指提供的
context
中合并overrides
或者 當前 上下文中合并overrides
,例如:# current context is {'key1': True} r2 = records.with_context({}, key2=True) # -> r2._context is {'key2': True} r2 = records.with_context(key2=True) # -> r2._context is {'key1': True, 'key2': True}
- Model.with_company(company)[源代碼]?
返回一個帶有修改上下文的新記錄集版本,使其如下所示:
result.env.company = company result.env.companies = self.env.companies | company
- 參數
company (
res_company
or int) – 新環境的主公司。
警告
當當前用戶使用未授權的公司時,在非sudo環境下訪問環境中的公司可能會觸發AccessError。
- Model.with_env(env)[源代碼]?
返回一個附加到提供環境的新版本記錄集。
- 參數
env (
Environment
) –
注解
返回的記錄集與
self
具有相同的預取對象。
SQL 執行?
在環境中, cr
屬性是當前數據庫事務的游標,允許直接執行 SQL,無論是為了難以使用 ORM 表達的查詢(例如復雜的連接),還是出于性能原因:
self.env.cr.execute("some_sql", params)
警告
執行原始 SQL 會繞過 ORM,從而繞過 Odoo 的安全規則。請確保在使用用戶輸入時對查詢進行過濾,并且如果您不真正需要使用 SQL 查詢,則優先使用 ORM 工具。
關于模型,需要知道的一件重要的事情是它們不一定立即執行數據庫更新。為了性能原因,框架會延遲在修改記錄后重新計算字段的操作。而且一些數據庫更新也會被延遲。因此,在查詢數據庫之前,必須確保它包含了查詢所需的相關數據。這個操作被稱為 flushing ,它執行了預期的數據庫更新。
Example
# make sure that 'partner_id' is up-to-date in database
self.env['model'].flush_model(['partner_id'])
self.env.cr.execute("SELECT id FROM model WHERE partner_id IN %s", [ids])
ids = [row[0] for row in self.env.cr.fetchall()]
在執行每個 SQL 查詢之前,必須刷新該查詢所需的數據。刷新有三個級別,每個級別都有自己的 API??梢运⑿滤袃热?、模型的所有記錄或某些特定記錄。由于延遲更新通??梢蕴岣咝阅?,因此建議在刷新時要 具體 。
- Model.flush_model(fnames=None)[源代碼]?
處理“self”模型上的待處理計算和數據庫更新。如果給定參數,則該方法保證至少將給定字段刷新到數據庫??梢运⑿赂嘧侄?。
- 參數
fnames – 可選的要刷新的字段名稱的可迭代對象
- Model.flush_recordset(fnames=None)[源代碼]?
處理記錄
self
上的掛起計算和數據庫更新。當給定參數時,該方法保證至少將記錄self
上的給定字段刷新到數據庫??梢运⑿赂嗟淖侄魏陀涗?。- 參數
fnames – 可選的要刷新的字段名稱的可迭代對象
因為模型使用相同的游標,而 Environment
持有各種緩存,因此在使用原始 SQL 修改數據庫時必須使這些緩存失效,否則進一步使用模型可能會變得不一致。在使用 SQL 的 CREATE
、 UPDATE
或 DELETE
時需要清除緩存,但不需要在使用 SELECT
時清除緩存(因為它只是讀取數據庫)。
Example
# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s", ['new', 'old'])
# invalidate 'state' from the cache
self.env['model'].invalidate_model(['state'])
與刷新類似,可以使整個緩存失效,使模型的所有記錄的緩存失效,或使特定記錄的緩存失效。甚至可以使某些記錄的特定字段或模型的所有記錄的特定字段失效。由于緩存通??梢蕴岣咝阅?,我們建議在使緩存失效時要 具體 。
- Environment.invalidate_all(flush=True)[源代碼]?
使所有記錄的緩存失效。
- 參數
flush – 是否在失效之前刷新掛起的更新。默認為
True
,可確保緩存一致性。除非您知道自己在做什么,否則不要使用此參數。
- Model.invalidate_model(fnames=None, flush=True)[源代碼]?
當緩存的值不再與數據庫的值相對應時,使
self
模型的所有記錄的緩存失效。如果給定參數,則僅從緩存中使給定字段失效。- 參數
fnames – 要使無效的字段名稱的可選可迭代對象
flush – 是否在失效之前刷新掛起的更新。默認為
True
,可確保緩存一致性。除非您知道自己在做什么,否則不要使用此參數。
- Model.invalidate_recordset(fnames=None, flush=True)[源代碼]?
當緩存的值不再對應數據庫的值時,使
self
中的記錄緩存失效。如果給定參數,則僅使self
上的給定字段的緩存失效。- 參數
fnames – 要使無效的字段名稱的可選可迭代對象
flush – 是否在失效之前刷新掛起的更新。默認為
True
,可確保緩存一致性。除非您知道自己在做什么,否則不要使用此參數。
上述方法可以使緩存和數據庫保持一致。但是,如果計算字段的依賴關系在數據庫中被修改,就必須通知模型重新計算計算字段??蚣苄枰赖奈ㄒ恍畔⑹?哪些 記錄上的 哪些 字段已經發生了變化。
Example
# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])
# use the RETURNING clause to retrieve which rows have changed
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s RETURNING id", ['new', 'old'])
ids = [row[0] for row in self.env.cr.fetchall()]
# invalidate the cache, and notify the update to the framework
records = self.env['model'].browse(ids)
records.invalidate_recordset(['state'])
records.modified(['state'])
需要找出哪些記錄已被修改。有許多方法可以做到這一點,可能涉及額外的 SQL 查詢。在上面的示例中,我們利用了 PostgreSQL 的 RETURNING
子句,在不進行額外查詢的情況下檢索信息。在通過失效使緩存一致之后,使用已更新的字段調用已修改記錄上的 modified
方法。
常見 ORM 方法?
創建/更新?
- Model.create(vals_list) records [源代碼]?
為模型創建新記錄。
新記錄將使用字典列表
vals_list
中的值進行初始化,如有必要,還將使用default_get()
中的值。- 參數
vals_list (Union[list[dict], dict]) – 模型字段的值,以字典列表形式提供:[{‘field_name’: field_value, …}, …]。為了向后兼容,
vals_list
可以是一個字典。它被視為單例列表[vals]
,并返回單個記錄。詳見write()
。- 返回
已創建的記錄
- 引發
AccessError – 如果當前用戶沒有權限創建指定模型的記錄
ValidationError – 如果用戶嘗試為選擇字段輸入無效值
ValueError – 如果在創建數值中指定的字段名稱不存在。
UserError – 如果在對象層次結構中創建了循環,操作的結果(例如將對象設置為其自身的父級)
- Model.copy(default=None)[源代碼]?
復制記錄
self
并使用默認值更新它- 參數
default (dict) – 在復制記錄的原始值中覆蓋的字段值字典,例如:
{'field_name': overridden_value, ...}
- 返回
新記錄
- Model.default_get(fields_list) default_values [源代碼]?
返回
fields_list
中字段的默認值。默認值由上下文、用戶默認值和模型本身決定。注解
未請求的默認值不會被考慮,不需要返回
fields_list
中未包含的字段的值。
- Model.name_create(name) record [源代碼]?
通過調用
create()
方法并提供一個值,即新記錄的顯示名稱,來創建一個新記錄。新記錄將使用適用于此模型的任何默認值進行初始化,或通過上下文提供。
create()
的通常行為適用。- 參數
name – 創建記錄的顯示名稱
- 返回類型
- 返回
創建記錄的
name_get()
對值
- Model.write(vals)[源代碼]?
使用提供的值更新
self
中的所有記錄。- 參數
vals (dict) – 需要更新的字段及其對應的值
- 引發
AccessError – 如果用戶沒有權限修改指定的記錄/字段
ValidationError – 如果選擇字段指定了無效值
UserError – 如果在對象層次結構中創建了循環,操作的結果(例如將對象設置為其自身的父級)
對于
Many2one
,值應該是要設置的記錄的數據庫標識符一個
One2many
或Many2many
關系字段的期望值是一個Command
列表,用于操作它們所實現的關系??偣灿?個命令:create()
,update()
,delete()
,unlink()
,link()
,clear()
和set()
。For
Date
和~odoo.fields.Datetime
,值應該是日期(時間)或字符串。警告
如果為日期(時間)字段提供字符串,則必須僅使用UTC并按照
odoo.tools.misc.DEFAULT_SERVER_DATE_FORMAT
和odoo.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
格式化。其他非關系型字段使用字符串作為值
搜索/讀取?
- Model.browse([ids]) records [源代碼]?
在當前環境中,返回提供的參數所對應的記錄集。
self.browse([7, 18, 12]) res.partner(7, 18, 12)
- Model.search(domain[, offset=0][, limit=None][, order=None][, count=False])[源代碼]?
基于
domain
search domain 搜索記錄。
- Model.search_count(domain) int [源代碼]?
返回當前模型中與 提供的篩選條件 匹配的記錄數。
- 參數
domain – 搜索域 。使用空列表匹配所有記錄。
limit – 最大記錄數(上限)(默認:全部)
- Model.name_search(name='', args=None, operator='ilike', limit=100) records [源代碼]?
根據給定的
name
模式和operator
比較條件,搜索符合可選搜索域(args
)的顯示名稱匹配的記錄。這通常用于基于關系字段的部分值提供建議。應該通常表現為
name_get()
的反向,但這并不保證。此方法相當于基于“display_name”調用
search()
方法,然后在搜索結果上調用name_get()
方法。
- Model.read([fields])[源代碼]?
讀取
self
中記錄的請求字段,低級/RPC 方法。- 參數
- 返回
一個字典列表,將字段名映射到它們的值,每個記錄一個字典
- 返回類型
- 引發
AccessError – 如果用戶無權訪問請求的信息
ValueError – 如果請求的字段不存在
- Model.read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True)[源代碼]?
按照給定的“groupby”字段分組獲取列表視圖中的記錄列表。
- 參數
fields (list) – 在對象指定的列表視圖中存在的字段列表。每個元素都是’字段’(字段名稱,使用默認聚合),或’字段:agg’(使用聚合函數’agg’的聚合字段),或’name:agg(field)’(使用’agg’的聚合字段,并將其作為’name’返回)??赡艿木酆虾瘮凳怯?`PostgreSQL <https://www.postgresql.org/docs/current/static/functions-aggregate.html>`_提供的,并且包括’count_distinct’,其含義如預期。
groupby (list) – 記錄將按照分組描述進行分組。分組描述可以是一個字段(那么它將按照該字段進行分組),也可以是一個字符串“field:granularity”。目前,唯一支持的粒度是“day”、“week”、“month”、“quarter”或“year”,并且它們只對日期/時間字段有意義。
offset (int) – 可選的跳過記錄數
limit (int) – 可選的最大返回記錄數
orderby (str) – 可選的
order by
規范,用于覆蓋組的自然排序順序,另請參見 :pysearch()
(目前僅支持many2one字段)lazy (bool) – 如果為真,則結果僅按第一個groupby分組,其余的groupby放在__context鍵中。如果為假,則所有groupby在一次調用中完成。
- 返回
包含字典列表(每個記錄一個字典),其中包含: * 按
groupby
參數分組的字段值 * __domain:指定搜索條件的元組列表 * __context:帶有groupby
參數的字典 * __range:(僅限日期/時間)字典,以field_name:granularity為鍵,映射到具有鍵的字典:”from”(包含)和”to”(不包含),映射到組的時間邊界的字符串表示- 返回類型
[{‘field_name_1’: value, …}, …]
- 引發
AccessError – 如果用戶無權訪問請求的信息
字段?
搜索域?
域是一個條件列表,每個條件都是一個三元組(可以是 list
或 tuple
),其中 (field_name, operator, value)
,其中:
field_name
(str
)當前模型的字段名稱,或通過點符號遍歷
Many2one
關系的字段名稱,例如'street'
或'partner_id.country'
operator
(str
)用于將
field_name
與value
進行比較的運算符。有效的運算符包括:=
等于
!=
不等于
>
大于
>=
大于或等于
<
小于
<=
小于或等于
=?
未設置或等于(如果
value
為None
或False
,則返回true,否則的話就像=
一樣)=like
使用
value
模式匹配field_name
。模式中的下劃線_
代表(匹配)任意單個字符;百分號%
匹配零個或多個字符的任意字符串。like
將
field_name
與%value%
模式進行匹配。類似于=like
,但在匹配前將value
用 ‘%’ 包裝not like
不符合
%value%
模式ilike
不區分大小寫的
like
not ilike
不區分大小寫的
not like
=ilike
不區分大小寫的
=like
in
等于
value
中的任何一項,value
應該是一個項目列表not in
與
value
中的所有項都不相等child_of
是
value
記錄的子級(后代)(value 可以是一個項目或項目列表)。考慮模型的語義(即遵循由
_parent_name
指定的關系字段)。parent_of
是
value
記錄的父級(祖先)(value 可以是一個項目或項目列表)。考慮模型的語義(即遵循由
_parent_name
指定的關系字段)。
value
變量類型,必須可以通過
operator
與命名字段進行比較。
域準則可以使用 前綴 形式的邏輯運算符進行組合:
'&'
邏輯 AND ,默認操作是將相互跟隨的條件組合在一起。Arity 2(使用接下來的2個條件或組合)。
'|'
邏輯 或 ,參數個數為2。
'!'
邏輯 非 ,參數個數為1。
注解
主要是為了否定條件的組合。通常,單個條件都有一個否定形式(例如,
=
->!=
,<
->>=
),這比否定肯定形式更簡單。
Example
搜索名為 ABC 的合作伙伴,來自比利時或德國,其語言不是英語的:
[('name','=','ABC'),
('language.code','!=','en_US'),
'|',('country_id.code','=','be'),
('country_id.code','=','de')]
該域被解釋為:
(name is 'ABC')
AND (language is NOT english)
AND (country is Belgium OR Germany)
取消鏈接?
- Model.unlink()[源代碼]?
刪除
self
中的記錄。- 引發
AccessError – 如果用戶沒有權限刪除所有給定的記錄
UserError – 如果該記錄是其他記錄的默認屬性
記錄(集合)信息?
- Model.ids?
返回與“self”對應的實際記錄ID列表。
- odoo.models.env?
返回給定記錄集的環境。
- 類型
- Model.name_get()[源代碼]?
返回
self
中記錄的文本表示形式,每個輸入記錄輸出一個項目,順序相同。警告
雖然
name_get()
可以使用上下文數據進行更豐富的上下文格式化,但由于它是display_name
的默認實現,如果上下文鍵為空/缺失,重置為“默認”行為非常重要。
- Model.get_metadata()[源代碼]?
返回有關給定記錄的一些元數據。
- 返回
每個請求記錄的所有權字典列表
- 返回類型
包含以下鍵的字典列表: * id:對象ID * create_uid:創建記錄的用戶 * create_date:記錄創建日期 * write_uid:最后更改記錄的用戶 * write_date:記錄最后更改日期 * xmlid:用于引用此記錄的XML ID(如果有),格式為“module.name” * xmlids:包含xmlid的字典列表,格式為“module.name”,并帶有noupdate布爾值 * noupdate:一個布爾值,指示記錄是否將被更新
操作?
記錄集是不可變的,但是可以使用各種集合操作來組合同一模型的集合,返回新的記錄集。
record in set
返回一個布爾值,指示record
(必須是一個只有一個元素的記錄集)是否存在于set
中。record not in set
是相反的操作。set1 <= set2
和set1 < set2
返回set1
是否是set2
的子集(嚴格子集)set1 >= set2
和set1 > set2
返回set1
是否是set2
的超集 (分別為嚴格和非嚴格)set1 | set2
返回兩個記錄集的并集,即包含兩個源中存在的所有記錄的新記錄集set1 & set2
返回兩個記錄集的交集,即一個新的記錄集,只包含兩個源中都存在的記錄set1 - set2
返回一個新的記錄集,其中僅包含set1
中不在set2
中的記錄
Recordsets是可迭代的,因此可以使用通常的Python工具進行轉換( map()
, sorted()
, ifilter()
,…),但這些工具返回的是一個 list
或一個 iterator ,因此無法在其結果上調用方法或使用集合操作。
因此,記錄集提供以下操作,返回記錄集本身(如果可能):
篩選?
- Model.filtered(func)[源代碼]?
返回滿足
func
的self
中的記錄。- 參數
func (callable or str) – 一個函數或由字段名用點號分隔的序列
- 返回
符合條件的記錄集,可能為空。
# 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")
地圖?
- Model.mapped(func)[源代碼]?
將
func
應用于self
中的所有記錄,并將結果作為列表或記錄集返回(如果func
返回記錄集)。在后一種情況下,返回的記錄集的順序是任意的。- 參數
func (callable or str) – 一個函數或由字段名用點號分隔的序列
- 返回
如果 func 為假,則返回 self;否則返回應用于所有
self
記錄的 func 的結果。- 返回類型
list or 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 records.mapped('partner_id') # returns the union of all partner banks, with duplicates removed records.mapped('partner_id.bank_ids')
注解
自V13版本開始,支持多關系字段訪問,并且與映射調用類似:
records.partner_id # == records.mapped('partner_id')
records.partner_id.bank_ids # == records.mapped('partner_id.bank_ids')
records.partner_id.mapped('name') # == records.mapped('partner_id.name')
排序?
繼承和擴展?
Odoo 提供了三種不同的機制以模塊化的方式擴展模型:
從現有模型創建一個新模型,向副本添加新信息,但保留原始模塊不變
在原地擴展在其他模塊中定義的模型,替換先前的版本
將模型的一些字段委托給其包含的記錄

經典繼承?
當同時使用 _inherit
和 _name
屬性時,Odoo 會創建一個新的模型,使用已有的模型 (通過 _inherit
提供) 作為基礎。新模型將從其基礎模型繼承所有字段、方法和元信息(默認值和其他)。
class Inheritance0(models.Model):
_name = 'inheritance.0'
_description = 'Inheritance Zero'
name = fields.Char()
def call(self):
return self.check("model 0")
def check(self, s):
return "This is {} record {}".format(s, self.name)
class Inheritance1(models.Model):
_name = 'inheritance.1'
_inherit = 'inheritance.0'
_description = 'Inheritance One'
def call(self):
return self.check("model 1")
并使用它們:
a = env['inheritance.0'].create({'name': 'A'})
b = env['inheritance.1'].create({'name': 'B'})
a.call()
b.call()
將產生:
“這是模型0記錄A” “這是模型1記錄B”
第二個模型繼承了第一個模型的 check
方法和 name
字段,但是覆蓋了 call
方法,就像使用標準的 Python繼承 一樣。
擴展?
當使用 _inherit
但是不指定 _name
時,新模型會替換現有模型,實質上是在原地擴展。這對于向現有模型(在其他模塊中創建的)添加新字段或方法,或者自定義或重新配置它們(例如更改它們的默認排序順序)非常有用:
class Extension0(models.Model):
_name = 'extension.0'
_description = 'Extension zero'
name = fields.Char(default="A")
class Extension1(models.Model):
_inherit = 'extension.0'
description = fields.Char(default="Extended")
record = env['extension.0'].create({})
record.read()[0]
將產生:
{'name': "A", 'description': "Extended"}
注解
它還將返回各種 自動字段 ,除非它們已被禁用。
委托?
第三種繼承機制提供了更多的靈活性(可以在運行時更改),但是功能較弱:使用 _inherits
屬性將一個模型“委托”給任何在當前模型上未找到的字段查找到“子”模型。委托是通過在父模型上自動設置的 Reference
字段來執行的。
主要區別在于含義。使用委托時,模型 擁有一個 而不是 是一個 ,將關系轉化為組合而不是繼承:
class Screen(models.Model):
_name = 'delegation.screen'
_description = 'Screen'
size = fields.Float(string='Screen Size in inches')
class Keyboard(models.Model):
_name = 'delegation.keyboard'
_description = 'Keyboard'
layout = fields.Char(string='Layout')
class Laptop(models.Model):
_name = 'delegation.laptop'
_description = 'Laptop'
_inherits = {
'delegation.screen': 'screen_id',
'delegation.keyboard': 'keyboard_id',
}
name = fields.Char(string='Name')
maker = fields.Char(string='Maker')
# a Laptop has a screen
screen_id = fields.Many2one('delegation.screen', required=True, ondelete="cascade")
# a Laptop has a keyboard
keyboard_id = fields.Many2one('delegation.keyboard', required=True, ondelete="cascade")
record = env['delegation.laptop'].create({
'screen_id': env['delegation.screen'].create({'size': 13.0}).id,
'keyboard_id': env['delegation.keyboard'].create({'layout': 'QWERTY'}).id,
})
record.size
record.layout
將導致:
13.0
'QWERTY'
并且可以直接在委托字段上進行編寫:
record.write({'size': 14.0})
警告
當使用委托繼承時,方法 不會 被繼承,只有字段會被繼承
警告
_inherits
已經或多或少地實現了,如果可以的話請避免使用;鏈式
_inherits
實際上未被實現,我們無法保證最終行為。
字段增量定義?
字段是在模型類上定義的類屬性。如果模型被擴展,也可以通過在子類上重新定義具有相同名稱和相同類型的字段來擴展字段定義。在這種情況下,字段的屬性來自父類,并被子類中給定的屬性覆蓋。
例如,下面的第二個類僅在字段“state”上添加了一個工具提示:
class First(models.Model):
_name = 'foo'
state = fields.Selection([...], required=True)
class Second(models.Model):
_inherit = 'foo'
state = fields.Selection(help="Blah blah blah")
錯誤管理?
Odoo 異常模塊定義了幾個核心異常類型。
這些類型被RPC層理解。任何其他異常類型在傳遞到RPC層之前都將被視為“服務器錯誤”。
注解
如果您考慮引入新的異常,請查看 odoo.addons.test_exceptions
模塊。
- exception odoo.exceptions.AccessDenied(message='Access Denied')[源代碼]?
登錄/密碼錯誤。
注解
沒有追蹤信息。
示例
當您嘗試使用錯誤的密碼登錄時。
- exception odoo.exceptions.RedirectWarning(message, action, button_text, additional_context=None)[源代碼]?
警告,可以重定向用戶而不僅僅是顯示警告消息。