1. 程式人生 > 實用技巧 >最好用的免費ERP系統Odoo 12開發手冊 | 第四篇 模組繼承

最好用的免費ERP系統Odoo 12開發手冊 | 第四篇 模組繼承

第四章 模組繼承

1. 本文為 最好用的免費ERP系統 Odoo 12開發手冊系列文章第四篇

2. Odoo的是一個強大功能是無需直接修改底層物件就可以新增功能. 
      (1) 這是通過其繼承機制來實現的,採取在已有物件之上修改層來完成.
      (2) 這種修改可以在不同層上進行 => 模型層,檢視層,業務邏輯層.
      (3) 我們新建立的模組來做出的 所需修改 而 無需再原有模組中直接修改.(是按需修改,而不是直接在模組上進行修改)

3. 第三篇文章,我們直接從零開始建立了一個新應用,本文我們將學習如何通過繼承已有的核心應用或第三方模組來建立新的模組
      實現以上本文將主要涵蓋: 
      (1) 原模型擴充套件,為已有模型新增功能
      (2) 修改資料記錄來繼承檢視,新增功能或修改資料來修改其他模組建立的資料記錄
      (3) 其他模型繼承機制,如代理繼承和 mixin 類
      (4) 繼承 Python 方法來為應用業務邏輯新增功能
      (6) 繼承Web控制器和模板來為網頁新增功能

開發準備

1. 本文要求可通過命令列來啟動Odoo服務.(這裡使用的康虎封裝的綠色版Odoo)
2. 程式碼將在第三章開發的library_app的基礎上進行修改.(主要做好程式碼的版本控制管理)

學習專案-繼承圖書館應用

1. 在第三章中我們建立了一個圖書應用的初始模組,可供檢視圖書目錄.

2. 現在我們要建立一個 library_member 模組,來對圖書應用進行擴充套件以讓圖書會員可以借書.  
      (1) 它繼承Book模型,並新增一個圖書是否可借的標記. 
      (2) 該資訊會在圖書表單和圖書目錄頁顯示.

3. 應新增圖書會員主資料模型 Member, 類似 Partner 來儲存個人資料,如姓名,地址和email,還有一些特殊欄位,如圖書會員卡號.
      (1) 最有效的方案是代理繼承,自動建立圖書會員記錄幷包含關聯 Partner 記錄. 
      (2) 該方案使得所有的 Partner 欄位在 Member 中可用,沒有任何資料結構上的重複.

4. 我們還要在借書表單中為會員提供訊息和社交功能,包括計劃活動元件來實現更好地協作. 
      我們還要新增會員從圖書館中借書的功能,但暫不涉及,以下是當前所要修改內容的總結: 
      (1) 圖書
            1) 新增一個 Is Available?欄位,現在通過手動管理,以後會自動化
            2) 擴充套件ISBN驗證邏輯來同時支援10位數的ISBN
            3) 擴充套件圖書目錄頁來分辨不可借閱圖書並允許使用者過濾出可借圖書
      (2) 會員
            1) 新增一個新模型來儲存姓名,卡號和Email,地址一類的聯絡資訊
            2) 新增社交討論和計劃活動功能
5. 首先在library_app同級目錄下(就是在同一層)建立一個 library_member 目錄來作為擴充套件模組
      下面是 __manifest__.py 的內容,注意, 這個擴充套件的模組不是一個app
{
    'name': 'Library Members',
    'description': 'Manage people who will be able to borrow books.',
    'author': 'Alan Hou',
    'depends': ['library_app'],
    'application': False,
}

原模型繼承

1. 第一步我們來為 Book模型新增 is_available 布林型欄位.
      (1) 這裡使用經典的 in-place 模型繼承.
      (2) 該欄位值可通過圖書借出和歸還記錄自動計算. 
      (3) 但現在我們先使用普通欄位.

2. 要繼承已有模型,需要在Python類中新增一個 _inherit 屬性來標明所繼承的模型.
      (1) 新類繼承父 Odoo 模型的所有功能, 僅需在其中宣告要做的修改. 
      (2) 在任何地方使用該模型修改都可用,可以認為這類繼承是對已有模型的應用並在原處做了一些修改.

為模型新增欄位

1. 通過Python類來新建莫i選哪個,繼承模型同樣是通過 Python 以及 Odoo自有的繼承機制,及 _inherit類屬性.
      (1) _inherit 類屬性表明所繼承的模型
      (2) 新的類繼承父 Odoo 模型的所有功能,僅需宣告要修改的部分
2. 編碼指南推薦為每個模型建立一個Python檔案,因此我們新增 library_member/models/library_book.py 檔案來繼承原模型
      (1) 新增 library_member/__init__.py 檔案匯入 models
from . import models
      (2) 新增 library_member/models/__init__.py
from . import library_book 
      (3) 建立library_member/models/library_book.py 檔案來繼承 library.book模型
from odoo import fields, models

class Book(models.Model):
    _inherit = 'library.book'
    is_available = fields.Boolean('Is Available?')

3. 
(1) 使用 _inherit類屬性來宣告所繼承模型.注意我們並沒有使用類屬性, 甚至是 _name也沒有使用.
      1) 除非你想做出修改,否則不需要使用這些屬性.
      2) _name是模型識別符號,如果修改的話,這時他會建立所繼承模型的拷貝,成為一個新模型,這個叫原型繼承,會在後面討論
(2) 可以把這個想成是對模型定義的一個引用,在原處做了一個修改. 
      1) 可以新增欄位,修改已有欄位,修改模型類屬性甚至包含業務邏輯的方法.

(3) 要在資料表中新增新的模型欄位,需要安裝該模組,如果一切順利,
      通過 Settings > Technical > Database Structure > Models選單檢視library.book模型即可看到該欄位
      可以直接檢視資料庫的資料表 library_book

修改已有欄位

1. 通過上面部分可以看到向已有模型新增新欄位非常簡單.
2. 有時還要對已有欄位進行修改,也非常簡單. 
      (1) 在繼承模型時,可對已有欄位疊加修改
      (2) 也就是說僅需定義要增加或修改的欄位屬性

3. 我們將對原來建立的 library_app 模組的Book模型做兩處簡單的修改: 
      (1) 為 isbn欄位新增一條提示,說明同時支援10位數的ISBN(稍後會實現該功能)
      (2) 為 publisher_id欄位新增資料庫索引,以提升搜尋效率

4. 編輯 library_member/models/library_book.py檔案,新增如下程式碼: 
class Book(models.Model):
...
    isbn = fields.Char(help="Use a valid ISBN-13 or ISBN-10.")
    publisher_id = fields.Many2one(index=True)
5. 上面對欄位進行指定屬性修改,未涉及的屬性不會被修改. 
      (1) 升級模組,進入圖書表單,將滑鼠懸停在ISBN欄位上,就可以看到所新增的提示資訊了. 
      (2) index=True這一修改不太容易發現, 可以通過 通過Settings > Technical > Database Structure > Models選單下的欄位定義中可進行檢視。

修改檢視和資料

1. 模組中檢視和其他資料構件也可通過繼承來修改. 
2. 就檢視而言,通常需要新增功能. 
3. 檢視的展示結構在 arch 欄位中使用XML定義.
      (1) 這一XML資料可通過定位到所需修改的地方來進行繼承,然後宣告需執行的操作,如在該處新增XML元素.
      (2) 對於剩餘的資料元素,它們代表寫入資料庫中的記錄,繼承模型可通過寫操作來修改它們的值.

繼承檢視

1. 表單,列表和搜尋檢視通過 arch XML結構定義. 
2. 要繼承檢視,就要一種修改XML的方式,也即定位XML元素然後對該處進行修改.
3. 檢視繼承的XML記錄 和 普通檢視中相似,多一個 inherit_id 屬性來引用所要繼承的檢視.
       下面我們來繼承圖書檢視並新增 is_available 欄位.

4. 首先要查詢待繼承的檢視的XML ID(外部ID),通過 Settings > Technical > User Interface > Views選單來檢視.
      (1) 圖書表單的 XML ID是 library_app.view_form_book
      (2) 然後還要找到要插入的XML元素,我們在ISBN欄位之後新增 IS Available? 
      (3) 通常通過 name 屬性定位,此處為 <field name="isbn"/>
5. 我們新增 views/book_view.xml 檔案來繼承 Partner檢視,加入如下內容
<?xml version="1.0"?>
<odoo>
    <record id="view_form_book_extend" model="ir.ui.view">
        <field name="name">Book: add Is Available? field</field>
        <field name="model">library.book</field>
        <field name="inherit_id" ref="library_app.view_form_book" />
        <field name="arch" type="xml">
            <field name="isbn" position="after">
                <field name="is_available" />
            </field>
        </field>
    </record>
</odoo>
      (1) inherit_id 記錄欄位通過 ref 屬性指向繼承檢視的外部識別符號, 我們將在第五章討論外部識別符號的詳情.
      (2) 檢視使用XML定義並存儲在結構欄位arch中, .

6. 要繼承一個檢視,先定位要擴充套件的節點,然後執行要做的操作,如新增XML元素. 
      (1) 定位節點的最簡單的方法是使用唯一標識屬性,通常是name. (檢視的name是唯一標識屬性)
      (2) 然後新增定位屬性,宣告要做的修改. 
      (3) 本例中 繼承節點(就是在哪裡加入) name='isbn'元素,修改是在選定元素後加一段XML:
<field name="isbn" position="after">
    <!-- 此處新增修改內容 -->
</field>

7. 除 string 屬性外的任意 XML元素和屬性都可作為繼承節點(一般就是選擇name). 
      (1) 字串屬性會被翻譯成使用者使用的語言,因此不能作為節點選擇器
      (2) 9.0之前,string屬性也作為繼承定位符,9.0之後則不再被允許

8. 一旦XML節點被選為繼承點,需要指明要執行的繼承操作. 這通過 position 屬性實現: 
      (1) inside(預設值): 在所選節點內新增內容,這一節點應是 <group> 或 <page>一類容器
      (2) after: 在選定節點之後向父節點新增內容. 
      (3) before: 在選定節點之前向父節點新增內容
      (4) replace: 替換所選節點. 若使用空元素這回刪除該元素. 通常在內容中使用 $0 來表示被替換的元素.
            小貼士: 通過 position="replace"可刪除XML元素,但應該避免這麼做. 一個替代方案是: 讓該元素不可見.      
      (5) attributes: 修改匹配元素屬性值. 
            1) 內容中應包含一個或多個<attribute name="attr-name">value</attribute>元素
            2) 如<attribute name=”invisible”>True</attribute>
            3) 若不帶內容,則attrubute會從所選元素中刪除(相當於替換了 空值)

9. 模型繼承 + 表單繼承 ==> 馬上就可以看到效果了(通過繼承的方式對原app的一個修改)

使用 XPath 選取繼承點

1. 有時可能沒有帶唯一值的屬性用作XML節點選擇器. 在所選元素沒有 name 屬性時可能出現這一情況: 
      (1) 如 <group> <notebook> 或 <page>檢視元素. 
      (2) 另外就是有多個帶有相同 name 屬性的元素,比如在看板 QWeb檢視中相同欄位可能在同一 XML模板中被多次包含. 

2. 如果不能使用name屬性定位,就需要用到 XPath表示式. 
3. 定位<field name="isbn">元素的 XPath表示式是 //field[@name]='isbn'       
      該表示式查詢 name 屬性等於 isbn 的 <field>元素
4. 前一部分對圖書表單檢視繼承的XPath寫法是: 
<xpath expr="//field[@name='isbn']" position="after">
    <field name="is_available" />
</xpath>
5. 如果 XPath 表示式匹配到多個元素,僅會選取第一個元素. 所以表示式應越精確越好,使用唯一屬性. 
      (1) name屬性最易於確保找到精確元素作為擴充套件點
      (2) 在建立檢視 XML元素時新增唯一識別符號就非常重要.

其他模型繼承機制

模型(models)的繼承在在 Odoo 中有幾種:
(1) 經典繼承 (2) 原型繼承(一般不使用) (3) 代理繼承(模型的組合關係) (4) 使用 mixin 類繼承

1. 前面我們介紹了模型的基本繼承,在官方文件中成為 經典繼承. 
      (1) 這是最常用的繼承方式,最容易想到的就是 in-place 繼承.
      (2) 獲取模型並對其繼承,新增的新功能會自動新增到已有模型中,而不會建立新模型.
2. 可以為 _inherit 屬性傳入多個值來繼承多個父模型. 
      (1) 大多數情況下是通過 mixin 類完成,mixin類是實現可複用的通用功能. 
      (2) 也可以像普通模型那樣獨立使用,像是一個功能容器,可隨時加到其他模型中.

3. 原型(prototype)繼承
      (1) 使用 _inherit 屬性的同時還使用了於父模型不同的 _name 屬性(_name是模型的唯一標識)
      (2) 此時會複用所繼承並建立一個新的模型,並帶有自己的資料表和資料

4. 代理(delegation)繼承,通過 _inherits 屬性來使用(注意最後有一個s).
      (1) 這允許我們建立一個包含和繼承已有模型的新模型.
      (2) 新模型建立新紀錄時,在原模型中也會被建立並使用 many-to-one 欄位關聯.
      (3) 檢視新模型的人可以看到所有原模型和新模型的欄位,但在後臺兩個模型分別處理各自的資料

使用原型繼承拷貝功能

1. 前文我們繼承模型時使用了 _inhert屬性,建立一個類繼承 library.book 並添加了一些功能.
      (1) 類中並沒有 _name 屬性,不指明即使用 library.book.
      (2) 如果設定了不個和父模型不同_name(_name是資料庫中的資料表名),會拷貝原模型建立一個新模型
2. 在實際開發中,這類繼承一般通過抽象mixin類,很少這樣直接繼承普通模型,因為這樣會建立冗餘的資料結構.
3. Odoo還有一種 代理繼承機制 可避免這類資料結構冗餘,所以普通逆行通常會使用這種方式來做繼承. 

使用代理繼承內嵌模型

1. 使用代理繼承無需複製資料即可在資料庫中複用資料結構,這通過將一個模型嵌入另一個來實現.
      (1) UML中這種稱作組合(composition)關係: 父類無需子類即可存在,而子類必須要有父類才能存在
      (2) 比如,核心User模型,每條記錄包含一條Partner記錄, 就是 User has a Partner. 
      (3) Partner類似於"CPU",可以獨立存在,User類似"電腦",沒有"CPU"是不能存在的

2. 在圖書專案中,我們要新增一個圖書會員模型(就是要有一張 圖書會員表).
      (1) 會員有會員卡並通過會員卡借閱圖書.
      (2) 我們要記錄卡號,還要儲存email和地址這類個人資訊.
      (3) 內建Partner模型已包含了聯絡和地址資訊,所以最好是進行復用,而不去建立重複的資料結構. (這個的前提是對Odoo的內部模型有了解)
3. 為會員模型建立 library_member/models/library_member.py 檔案並加入如下程式碼: 
from odoo import fields, models

class Member(models.Model):
    _name = 'library.member'
    _description = 'Library Member'
    card_number = fields.Char()
    partner_id = fields.Many2one(
        'res.partner',
        delegate=True,
        ondelete='cascade',
        required=True)
4.
(1) 使用代理繼承(模型之間的組合),library_member中嵌入了繼承模型 res.partner.
      1) 在建立會員記錄時,一個關聯的Patner會自動被建立並通過 partner_id 欄位引用.
      2) delegate=True是需要的,就是聲明瞭代理繼承

(2) 透過代理機制,巢狀模型的所有欄位就像父模型欄位一樣自動可用.
      1) 在本例中,會員卡模型可使用Patner中的所有欄位如 name,address和email,以及自身獨有的欄位,如 card_number.
      2) 在後臺中,Partner欄位儲存在關聯的Partner記錄,沒有重複的資料結構.
      3) 注意: 對於模型方法則並非如此, Partner模型中的方法在 Member 模型中不可使用.


5. 
(1) 不要忘記在 library_member/models/__init__.py檔案中加入:
from . import library_book
from . import library_member
(2) 要使用我們建立的模型,還要完成以下步驟: 
      1) 新增安全許可權控制列表(ACL)
要建立安全ACL,建立 library_member/security/ir.model.access.csv檔案並加入如下程式碼: (還是分為兩個組)
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_member_user,Member User Access,model_library_member,library_app.library_group_user,1,1,1,0
access_member_manager,Member Manager Access,model_library_member,library_app.library_group_manager,1,1,1,1
      2) 新增選單項
要新增選單項,建立 library_member/views/library_menu.xml檔案並加入如下程式碼: 
<odoo>
    <act_window id="action_library_member"
        name="Library Members"
        res_model="library.member"
        view_mode="tree,form" />
    <menuitem id="menu_library_member"
        action="action_library_member"
        parent="library_app.menu_library" />
</odoo>
      3) 新增表單和列表檢視
要新增檢視,建立 library_member/views/member_view.xml檔案並加入如下程式碼: 
<?xml version="1.0" ?>
<odoo>
    <record id="view_form_member" model="ir.ui.view">
        <field name="name">Library Member Form View</field>
        <field name="model">library.member</field>
        <field name="arch" type="xml">
            <form>
                <group>
                    <field name="name" />
                    <field name="email" />
                    <field name="card_number" />
                </group>
            </form>
        </field>
    </record>
    <record id="view_tree_member" model="ir.ui.view">
        <field name="name">Library Member List View</field>
        <field name="model">library.member</field>
        <field name="arch" type="xml">
            <tree>
                <field name="name" />
                <field name="card_number" />
            </tree>
        </field>
    </record>
</odoo>
      4) 更新 manifest 檔案來宣告這些新增資料檔案
    'data':[
...
        'security/ir.model.access.csv',
        'views/library_menu.xml',
        'views/member_view.xml',
    ]
      (5) 如果編寫正確,在進行模型更新後即可使用新的圖書會員模型了

使用 mixin 類繼承模型

1. 原型繼承主要用於支援 mixin類.
2. mixin是基於 models.Abstract 的抽象的模型(而不是models.Model)
      (1) 他在資料庫中沒有實際的體現,而是提供功能供其他模型複用(混合 mixed in)
3. Odoo外掛提供多種mixin, 最常用的兩種由 Discuss應用(mail 模組)提供:
      (1) mail.thread 提供在許多文件表單下方或右側的訊息面板功能,以及訊息和通知相關邏輯.
      (2) mail.activity.mixin 提供代辦任務計劃.
4. 我們一起為 Member 模型新增上述這兩種 mixin. 
      社交訊息模組由 mail 模組的 mail.thread 模型提供,要將其加入自定義模型,應進行如下操作: 
      (1) 擴充套件模型須在 __manifest__.py 檔案中新增對mail的依賴
      'depends': ['library_app', 'mail'],
      (2) 第二部中對 mixin 類的繼承通過 _inherit 屬性完成,應編輯 library_member/models/library_member.py 並新增如下程式碼: 
class Member(models.Model):
    _name = 'library.member'
    _description = 'Library Member'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    ...
    通過新增額外的這行程式碼,我們的模型就會包含這些 mixin 的所有欄位和方法.
      (3) 第三步,向表單檢視新增相關欄位,編輯 library_member/views/member_view.xml檔案並在表單最後新增如下程式碼:  
<odoo>
...
            <form>
...
                <!-- mail mixin fields -->
                <div class="oe_chatter">
                    <field name="message_follower_ids" widget="mail_followers" />
                    <field name="activity_ids" widget="mail_activity" />
                    <field name="message_ids" widget="mail_thread" />
                </div>
            </form>

繼承Python方法

1. Python方法中編寫的業務邏輯也可以被繼承. Odoo借用了Python已有父類行為的物件繼承機制.
2. 作為一個實際的例子,我們將繼承圖書ISBN驗證邏輯.
      (1) 在圖書應用中僅能驗證13位的ISBN,但老一些的圖書可能只有10位數的ISBN.
      (2) 我們將繼承 _check_isbn() 方法來完成這種情況的驗證. (也是通過繼承的方式來修改/新增已有的功能)
      (3) 在 library_member/models/library_book.py 檔案中新增如下方法: 
from odoo import api, fields, models

class Book(models.Model):
...

    @api.multi
    def _check_isbn(self):
        self.ensure_one()
        isbn = self.isbn.replace('-', '')
        digits = [int(x) for x in isbn if x.isdigit()]
        if len(digits) == 10:
            ponderators = [1, 2, 3, 4, 5, 6, 7, 8, 9]
            total = sum(a * b for a, b in zip(digits[:9], ponderators))
            check = total % 11
            return digits[-1] == check
        else:
            return super()._check_isbn()
      1) 要繼承方法,我們要重寫該方法,可以使用 super() 來呼叫以實現的部分.
      2) 這個方法中我們驗證了是否為10位數ISBN,若不是10位,則進入原有的13位驗證邏輯. 

繼承Web控制器和模板

1. Odoo中的所有功能都是帶有擴充套件性, web功能也不例外,所有已有 控制器 和 模組 都能被繼承.
2. 作為示例,我們將繼承 圖書目錄 網頁,加入前面新增的圖書可用性資訊:
      (1) 在控制器端新增對查詢引數的支援,訪問 /library/books?available=1 過濾出可借閱圖書
      (2) 在模板端,新增一個圖書不可用的表示

繼承網頁控制器

1. 網頁控制器不應包含實際業務邏輯,僅集中於展示邏輯.
      (1) 我們可能會需要新增對額外的URL引數甚至是路由的支援,來改變網頁的展示
      (2) 我們將擴充套件 /library/books來支援 available=1 引數,以過濾出可借閱的圖書.

2. 要繼承已有控制器,需匯入對應物件,然後用方法新增邏輯來進行實現.
      下面新增 library_member/controllers/main.py 檔案並加入如下程式碼: 
from odoo import http
from myaddons.library_app.controllers.main import Books

class BookExtended(Books):
    @http.route()
    def list(self, **kwargs):
        response = super().list(**kwargs)
        if kwargs.get('available'):
            Book = http.request.env['library.book']
            books = Book.search([('is_available', '=', True)])
            response.qcontext['books'] = books
        return response

3. 
(1) 我們要繼承的Books控制器在 library_app/controllers/main.py 中定義. 
      1) 需要通過 myaddons.library_app.controllers.main 匯入
      2) 這和模型不同,模型可以通過 env 物件中的 central registry 來引用任意模型類,而無需瞭解實現它的檔案
      3) 控制器沒有這個,我們需要知道實現繼承控制器的模板和檔案.
(2) 基於Books聲明瞭一個 BooksExtended 類,類名不具有關聯性,僅用於繼承和擴充套件原類中定義的方法.
(3) 我們(重)定義了一個控制器方法 list(). 
      1) 它至少需要一個簡單的 @http.route() 裝飾器來保持路徑活躍.
      2) 如果不帶引數,將會保留父類中定義的路由. 
      3) 但也可以為 @http.route() 裝飾器新增引數,來重新定義或替換類路由
(4) 在繼承的list()方法中,一開始使用了 super()來執行已有的程式碼.
      1) 處理結果返回一個 Response 物件,Response 帶有模組要渲染的屬性 template,以及渲染使用的上下文 qcontext.
      2) 但還需要生成HTML,竟會在控制器結束執行時生成.
      3) 我們可以在最終渲染完成之前修改 Response屬性

(5) list()方法帶有 **kwargs引數,捕獲所有kwargs字典中的引數.
      1) 這是 URL 中的引數,如 ?available=1,
      2) 方法檢測 kwargs 中 available鍵的值,檢測到後改變 qcontext 來獲取僅為可借閱圖書的圖書記錄集

繼承QWeb模板

1. 要修改網頁的實際展示,就需要繼承所使用的QWeb模板. 
2. 我們將繼承 library_app.book_list_template 來展示更多有關不可借閱圖書的資訊.
3. 新增 library_member/views/book_list_template.xml 檔案並加入如下程式碼:
<odoo>
    <template id="book_list_extended"
        name="Extended Book List"
        inherit_id="library_app.book_list_template">
        <xpath expr="//span[@t-field='book.publisher_id']" position="after">
            <t t-if="not book.is_available">
                <b>(Not Available)</b>
            </t>
        </xpath>
    </template>
</odoo>
      (1) 網頁模板先其他Odoo檢視型別一樣時XML檔案,同樣可以使用XPath來定位元素並對它們進行操作.
      (2) 所繼承模型通過在元素中的 inherit_id指明
      (3) 這裡使用了靈活性很強的XPath標記,但是可以等價使用 <span t-field="book.publisher_id" position="after">      
4. 然後訪問 http://localhost:8269/library/books 即可對不可借閱圖書展示額外的(Not Available)資訊       

總結


.xml檔案的繼承 和 .py檔案的繼承

1. 擴充套件性是Odoo框架的一個重要功能. 
      我們可以建立外掛來為需要實現功能的多個層的已有外掛修改或新增功能.      

2. 模型層中: 
      (1) 我們使用 _inherit 模型屬性來引用已有模型,然後在原處執行修改.
      (2) 模型類的欄位物件還支援疊加定義,這樣可對已有欄位重新宣告,僅修改屬性

3. 其他模型繼承機制允許我們利用 資料結構 和 業務邏輯.            
      (1) 代理繼承通過多對一關聯欄位上的 delegate=True 屬性,來讓所有關聯模組的所有欄位可用,並複用它們的資料結構
      (2) 原型繼承使用 _inherit 屬性,來複制其他模型的功能(資料結構定義和方法),
      (3) 啟用抽象mixin類,提供一系列像文件討論資訊 和 follower 的可複用功能.            

4. 檢視層中: 
      (1) 檢視結構通過XML定義,(使用XPath或Odoo簡化語法)定位XML元素來進行繼承及新增XML片段.
      (2) 其他由模組建立的記錄已可由繼承模組修改,僅需引用對應的完整 XML ID 並在設想的欄位上執行寫操作  
5. 業務邏輯層中: 
      (1) 可使用模型繼承相同的機制來進行繼承,以及重新宣告要繼承的方法.
      (2) 在方法內,Python的 super() 函式可以呼叫所繼承方法的程式碼,新增程式碼可在其之前或之後執行.

6. 前端網頁(QWeb):
      (1) 網頁模板也是包含XML的結構的檢視,因此可以像其他檢視型別一樣的被繼承.              
          
下一篇文章中,我們將更深入學習模型,探索模型提供給我們的所有功能.

學霸專區

1. 如何繼承已有模型來新增 mixin,如mail.thread?
      有三個步驟來實現新增 mixin
      (1) 通過 mixin 模型 mail 位外掛新增依賴
      'depends': ['library_app', 'mail'],
      (2) 讓類繼承 mail.thread 和 mail.activity.mixin 兩個 mixin 類

class Member(models.Model):
    _name = 'library.member'
    _description = 'Library Member'
    _inherit = ['mail.thread', 'mail.activity.mixin']
...
      (3) 將 message_fllower_ids,message_ids 和 activity_id 這些 mixin 的資料欄位新增道表單檢視

<odoo>
...
            <form>
...
                <!-- mail mixin fields -->
                <div class="oe_chatter">
                    <field name="message_follower_ids" widget="mail_followers" />
                    <field name="activity_ids" widget="mail_activity" />
                    <field name="message_ids" widget="mail_thread" />
                </div>
            </form>                  

2. 要在會員表單檢視中新增Phone欄位需要做哪些修改?
      (1) 首先找要繼承檢視的 XML ID, <field name="inherit_id" ref="library_app.view_form_book" />
      (2) 一般通過name 屬性進行定位,然後進行新增
        <field name="arch" type="xml">
            <field name="isbn" position="after">
                <field name="phone" />
            </field>
        </field>      

3. 如果建立一個與繼承屬性的屬性名不同的模型類會發生什麼(例如_name=’y’ and _inherit=’x’)?
      這種就是原型繼承,就是會新建一個y表,但是內容會複製x一份.
      一般不會使用原型繼承,因為會造成資料結構的冗餘,一般是使用 代理繼承 來避免這種冗餘.      

4. XPath是否可用於修改其它模組的資料記錄?

5. 繼承一個模型時,是否可擴充套件其方法但不使用 super()呼叫所繼承的原始程式碼?

6. 如何在不引用任何特定欄位名的情況下繼承圖書目錄頁並在末行新增 ISBN 欄位?