第13章:繼承?
Odoo 的一個強大之處在于其模塊化。每個模塊都專注于一個業務需求,但模塊之間也可以相互交互。這對于擴展現有模塊的功能非常有用。例如,在我們的房地產場景中,我們希望直接在常規用戶視圖中顯示銷售人員的房產清單。
在深入了解特定的Odoo模塊繼承之前,讓我們先看看如何修改標準的CRUD(創建、檢索、更新或刪除)方法的行為。
Python繼承?
注解
目標 :本節結束時:
不應該刪除不是新建或取消的屬性。

當一個報價被創建時,物業狀態應該變為“已收到報價”
不應該創建一個價格比現有報價更低的報價

在我們的房地產模塊中,我們從未需要開發任何特定的內容來執行標準的CRUD操作。Odoo框架提供了必要的工具來執行它們。實際上,這些操作已經包含在我們的模型中,這要歸功于經典的Python繼承:
from odoo import fields, models
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
...
我們的 class TestModel
繼承自 Model
,該類提供了 create()
、 read()
、 write()
和 unlink()
方法。
這些方法(以及在 Model
上定義的任何其他方法)可以擴展以添加特定的業務邏輯:
from odoo import fields, models
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
...
@api.model
def create(self, vals):
# Do some business logic, modify vals...
...
# Then call super to execute the parent method
return super().create(vals)
對于 create()
方法,需要使用裝飾器 model()
,因為在創建記錄時,記錄集 self
的內容并不重要,但對于其他 CRUD 方法則不需要。
需要注意的是,即使我們可以直接覆蓋 unlink()
方法,你幾乎總是需要使用裝飾器 ondelete()
編寫一個新的方法。使用此裝飾器標記的方法將在 unlink()
方法期間被調用,避免了在直接覆蓋 unlink()
方法時可能出現的一些問題,特別是在卸載模型模塊時。
在Python 3中, super()
等同于 super(TestModel, self)
。當您需要使用修改后的記錄集調用父方法時,后者可能是必要的。
危險
始終調用
super()
非常重要,以避免破壞流程。只有極少數特定情況下,您不需要調用它。請確保始終返回與父方法一致的數據。例如,如果父方法返回一個
dict()
,則您的覆蓋方法也必須返回一個dict()
。
Exercise
在CRUD方法中添加業務邏輯。
如果屬性的狀態不是’New’或’Canceled’,則防止刪除該屬性
提示:使用 ondelete()
裝飾器創建一個新的方法,并記住 self
可以是一個包含多個記錄的記錄集。
在創建報價時,將屬性狀態設置為“已收到報價”。如果用戶嘗試創建低于現有報價的報價,則引發錯誤。
提示: property_id
字段在 vals
中可用,但它是一個 int
類型。要實例化一個 estate.property
對象,請使用 self.env[model_name].browse(value)
( 示例)
模型繼承?
參考 : 有關此主題的文檔可以在 繼承和擴展 中找到。
在我們的房地產模塊中,我們希望在設置/用戶和公司/用戶表單視圖中直接顯示與銷售員相關聯的屬性列表。為此,我們需要向“res.users”模型添加一個字段,并調整其視圖以顯示它。
Odoo提供了兩種 繼承 機制,以模塊化的方式擴展現有模型。
第一種繼承機制允許模塊通過以下方式修改另一個模塊中定義的模型的行為:
向模型添加字段,
覆蓋模型中字段的定義,
向模型添加約束條件,
向模型添加方法,
在模型中覆蓋現有方法。
第二種繼承機制(委托)允許將模型的每個記錄鏈接到父模型的記錄,并提供對該父記錄字段的透明訪問。

在Odoo中,第一種機制是最常用的。在我們的情況下,我們想要向現有模型添加一個字段,這意味著我們將使用第一種機制。例如:
from odoo import fields, models
class InheritedModel(models.Model):
_inherit = "inherited.model"
new_field = fields.Char(string="New Field")
一個實際的例子,在一個模型中添加了兩個字段,可以在這里找到 這里.
按照慣例,每個繼承的模型都在自己的 Python 文件中定義。在我們的例子中,它將是 models/inherited_model.py
。
Exercise
向用戶添加一個字段。
在
res.users
中添加以下字段:
字段 |
類型 |
---|---|
屬性 ID |
|
在字段中添加域,以便僅列出可用的屬性。
在下一節中,讓我們將該字段添加到視圖中并檢查一切是否正常!
視圖繼承?
參考 : 有關此主題的文檔可以在 繼承 中找到。
注解
目標 :本節結束時,銷售員用戶表單視圖中應顯示與其關聯的可用屬性列表

Odoo提供視圖繼承,而不是直接修改現有視圖(通過覆蓋它們)。子視圖可以在根視圖之上應用’擴展’視圖。這些擴展可以添加和刪除其父視圖的內容。
擴展視圖使用 inherit_id
字段引用其父視圖。其 arch
字段不是單個視圖,而是包含多個 xpath
元素,用于選擇和修改其父視圖的內容:
<record id="inherited_model_view_form" model="ir.ui.view">
<field name="name">inherited.model.form.inherit.test</field>
<field name="model">inherited.model</field>
<field name="inherit_id" ref="inherited.inherited_model_view_form"/>
<field name="arch" type="xml">
<!-- find field description and add the field
new_field after it -->
<xpath expr="//field[@name='description']" position="after">
<field name="new_field"/>
</xpath>
</field>
</record>
expr
一個XPath_表達式,在父視圖中選擇一個單獨的元素。如果匹配不到元素或匹配到多個元素,則會引發錯誤。
position
要應用于匹配元素的操作:
inside
將
xpath
的內容添加到匹配元素的末尾replace
使用
xpath
的主體替換匹配的元素,將新主體中的任何$0
節點出現替換為原始元素before
將
xpath
的主體作為匹配元素之前的同級插入after
將
xpaths
的主體作為匹配元素之后的同級插入attributes
使用
xpath
主體中的特殊attribute
元素更改匹配元素的屬性
當匹配單個元素時,可以直接在要查找的元素上設置 position
屬性。下面的兩個繼承都具有相同的結果。
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" />
</xpath>
<field name="description" position="after">
<field name="idea_ids" />
</field>
可以在此處找到視圖繼承擴展的示例 here.
由于其模塊化概念,繼承在Odoo中被廣泛使用。請不要猶豫,閱讀相應的文檔以獲取更多信息!
在 下一章節 中,我們將學習如何與其他模塊進行交互。