Odoo 開發通常都需要創建自己的插件模塊。本文中我們將通過創建第一個應用來一步步學習如何在 Odoo 中開啟和安裝這個插件。我們將從基礎的開發流學起,即創建和安裝新插件,然后在開發迭代中更新代碼來進行升級。
Odoo 采用類 MVC(Model-View-Controller)的結構,我們將深入到各層來實施一個圖書應用。本文主要內容有:
- 創建一個新的模塊,用來實施相關功能
- 添加應用的特性功能:頂級菜單項和安全組
- 添加一個一開始會失敗但在項目完成時成功運行的自動化測試
- 實施模型層,定義應用的數據結構和相關訪問權限
- 實施后台視圖層,編寫內部用戶界面
- 實施業務邏輯層,支持數據驗證和自動化
- 實施 web 層,展示訪客和內部用戶的用戶界面
系統准備
本文要求安裝了 Odoo 服務並可通過命令行啟動服務來進行模塊安裝和運行測試之類的操作。如果還沒有相關環境,請參照本系列文章第二篇Odoo 12開發之開發環境准備。
本文中我們將從零開始創建第一個 Odoo 應用,無需額外的代碼。本文代碼可通過 GitHub 倉庫進行查看。
概覽圖書項目
為更好地在本文中探討,我們將使用一個現實中可以使用的學習項目。一起來創建一個管理圖書庫的 Odoo 應用。該項目將在后續文章中持續使用,每篇文章都會進行一次迭代,為應用添加新的功能。本文中將創建圖書應用的第一個版本,第一個功能是實現圖書目錄。
圖書將包含如下數據:
- 標題
- 作者
- 出版社
- 發行日期
- 封面圖
- ISBN:包含檢查 ISBN是否有效的功能
- 有效性標記;標識圖書是否已對公眾發布
圖書目錄可由圖書管理員編輯,對圖書操作者則僅有可讀權限。該目錄可通過公共網頁訪問,僅顯示已發布圖書。就是這樣一個簡單的項目,但提供有用的功能,足以讓我們了解 Odoo 應用的主要構件。
創建新的插件模塊
一個插件模塊是包含實現一些 Odoo 功能的文件夾,可以添加新功能或修改已有的功能。插件目錄必須含有一個聲明或描述文件__manifest__.py,以及其它模塊文件。
一部分模塊插件在 Odoo 中以app的形式出現,通常都會帶有頂級菜單項。它們為 CRM 或 HR 這樣的功能區添加核心元素,因此在 Odoo 應用菜單中會高亮顯示。另外還有一些非應用模塊插件一般為這些應用添加功能。如果你的模塊為 Odoo 添加新的或重要的功能,一般應該是app。而如果模塊僅修改應用的功能,那么就是一個普通的插件模塊。
要創建新模塊,需要:
- 確保操作的目錄是 Odoo 的 addons 路徑
- 創建模塊目錄,並包含聲明文件
- 可選擇為模塊添加一個圖標
- 如打算對外發布,為模塊選擇一個證書
然后我們就可以安裝模塊了,確定模塊在 Odoo 服務中可見並正確安裝它。
准備 addons 路徑
一個插件模塊是一個含有 Odoo 聲明文件的目錄,它創建一個新應用或為已有應用添加功能。addons模塊的路徑是一系列目錄,Odoo 服務可以在這里查找插件。默認addons包含odoo/addons 中存放的 Odoo 自帶的官方應用,以及在odoo/odoo/addons目錄中提供核心功能的 base 模塊。
我們應將自己創建的或應用市場及其它地方下載的模塊放到指定的目錄中。要使得 Odoo 服務能夠找到這些應用,需要這些目錄添加到 Odoo 的 addons 路徑中。
根據我們在Odoo 12開發之開發環境准備所創建的項目,Odoo 的代碼存放在~/odoo-dev/odoo/目錄下。最佳實踐告訴我們應在自有目錄下添加代碼,而不應與 Odoo 源代碼混在一起。所以要添加自定義模塊,我們將在 Odoo 同級創建目錄~/odoo-dev/custom-addons並添加到 addons 路徑中。要添加該目錄至 addons 路徑,執行如下命令
注:如有報錯參照Odoo常見問題匯總開發類錯誤處理001
–save 參數將選項保存至配置文件中,這樣我們就無需在每次啟動服務時輸入參數,只需運行./odoo-bin 即可使用上次使用的參數。可以通過-c 參數指定文件來使用或保存配置項。仔細查看輸出的日志,可以看到INFO ? odoo: addons paths:[…] 一行中包含custom-addons目錄。
如需使用其它目錄也請添加至 addons 路徑,比如有~/odoo-dev/extra 目錄中包含需用到的目錄,則需通過如下方式設置–addons-path參數:
現在我們需要讓 Odoo 實例能識別新模塊。
小貼士:以上使用的是相對路徑,但在配置文件中需使用絕對路徑,–save 參數會自行進行轉化。
創建模塊目錄和聲明文件
現在就准備好了~/odoo-dev/custom-addons目錄,已正確添加至 addons 路徑,Odoo 也就可以找到這里的模塊。Odoo 自帶一個scaffold命令可自動創建新模塊目錄,其中會包含基礎結構。此處並不會使用該命令,而是手動創建。通過以下命令可以了解scaffold用法:
Odoo 模塊目錄需包含一個__manifest__.py描述性文件,同時還需要是可導入的包,所以還應包含__init__.py文件。
ℹ️在老版本中,該描述性文件為__openerp__.py或__odoo__.py,這些名稱已過時但仍可使用。
模塊目錄名是其技術名稱,我們使用library_app,技術名稱應是有效 Python 標識符,即以字母開頭且僅能包含字母、數字和下划線。執行如下步驟來初始化新模塊:
1、通過命令行,我們可以添加一個空的__init__.py 文件來初始化模塊:
2、下面添加聲明文件,其中應包含一個 Python 字典,有幾十個可用屬性。其中僅 name屬性為必填,但推薦同時添加 description 和 author 屬性。在__init__.py 同級創建__manifest__.py 文件,添加以下內容:
depends 屬性可以是一個包含所用到的模塊列表。Odoo 會在模塊安裝時自動安裝這些模塊,這不是強制屬性,但建議使用。如果沒有特別的依賴,可以添加內核 base 模塊。應注意將所有依賴都在此處列明,否則,模塊會因缺少依賴而報錯或出現加載錯誤(如果碰巧依賴模塊在隨后被加載了)。
我們的應用無需依賴其它模塊,所以本處使用了 base。為保持簡潔,這里僅使用了幾個基本描述符鍵:
- name:插件模塊標題字符串
- description:功能描述長文件,通常為RST格式
- author:作者姓名,本處為一個字符串,可以是逗號分隔的一系列姓名
- depends:一個依賴插件模塊列表,在模塊安裝時會先安裝這些插件
- application:一個布爾型標記,代表模塊是否在應用列表中以 app 展現
description可由模塊頂層目錄中的README.rst或README.md代替,如果兩者都不存在,將使用聲明文件中的description。
在真實場景中,建議也同時使用其它屬性名,因它們與 Odoo 的應用商店有關:
- summary:顯示為模塊副標題的字符串
- version::默認為1.0,應遵守版本號規則。建議在模塊版本號前加上 Odoo 版本,如12.0.1.0
- license::默認為LGPL-3
- website:了解模塊更多信息的 URL,可以幫助人們查看更多文檔或提供文件 bug 和建議的跟蹤
- category::帶有模塊功能性分類字符串,缺省為Uncategorized。已有分類可通過安全組表單(位於Settings > Users & Companies > Groups)的 Application字段下拉列表查看(需開啟調試模式)
還有以下描述符鍵:
- installable:默認為 True,但可以通過設置為 False 來禁用模塊
- auto_install:若設置為 True,在其依賴已安裝時會自動安裝,用於膠水模塊,用於同一實例上兩個模塊安裝后功能的連接。
添加圖標
模塊可選擇添加圖標,這對於作為 app 的模塊尤其重要,因為在應用菜單中一般都應有圖標。要添加圖標,需要在模塊中添加static/description/icon.png文件。
為簡化操作,我們可以復用 accounting 應用的圖標,把odoo/addons/account/static/description/icon.png文件拷貝至customaddons/library_app/static/description目錄。可通過如下命令完成:
補充:開啟開者發者模式(修改 URL 中web#為web?debug#),點擊 Apps > Update Apps List即可搜到我們創建的應用(下圖我使用了自定義的圖標)
選擇證書(開源協議)
為開發的模塊選擇證書(開源協議)非常重要,應謹慎考慮其代表着什么。Odoo 模塊最常用的協議是LGPL(GNU Lesser General Public License)第3版(LGPL v3.0)和AGPL(Affero General Public License)。
LGPL 授權更廣,它允許在無需分享相應源碼的情況下對代碼作出商業修改。AGPL則是一個更嚴格的開源證書,它要求派生代碼及服務托管者分享源碼。
了解更多有關 GNU 證書請訪問GNU官網。
安裝新模塊
現在我們已經有了一個簡化的模塊,還沒有任何功能,但我們可以通過安裝它來檢查各項是否正常。
要進行這一操作,模塊所有的插件目錄應對 Odoo 服務可見。可以通過啟動 Odoo 服務來進行確認,可以在輸出第一行看到顯示為odoo: addons paths: xxx 字樣,其中會顯示在用的插件路徑。更多有關插件路徑的知識,參見本系列文章第二篇 Odoo 12開發之開發環境准備。
要安裝新的模塊,我們應在啟動服務時指定-d 和-i 參數,-d 指定應使用的數據庫,-i 可接收一個逗號分隔的多個待安裝模塊名。假定開發數據庫為dev12,則使用如下命令進行安裝:
仔細看日志輸出可確定模塊是否能被找到並安裝,正確安裝對應日志: odoo.modules.registry: module library_app: creating or updating database tables。
更新模塊
開發模塊是一個不斷迭代的過程,我們會需要應用更新所修改代碼並在 Odoo 中可見。可以在后台界面Apps中搜索對應模塊並點擊 Upgrade 按鈕。但如果修改的是 Python 代碼,點擊升級不會生效,需要先重啟服務方可生效。這是因為 Odoo 僅會加載一次 Python 代碼,此后的修改就要求進行重啟才會生效。
有時,模塊中既修改了數據文件又修改了 Python 代碼,那么就需要同時進行如上兩種操作。這是 Odoo 開發者的常見困惑。幸好還有更好的方式,最保險的方式是重啟 Odoo 實例並應用升級至開發數據庫。通過Ctrl + C停止服務實例,然后通過如下命令啟動服務並升級library_app模塊:
-u(或全稱–update)要求使用-d 參數並接收一個逗號分隔的待升級模塊集。例如可以使用-u library_app,mail。模塊升級后,所有依賴該模塊的模塊也會被升級。這是保持用於擴展功能的繼承機制完整性的基礎。
Odoo 11中的修改:
直到 Odoo 10.0,要安裝新的插件模塊,需要在后台客戶端菜單中手動更新以對 Odoo 可見。從 11.0開始,模塊列表在模塊安裝或更新時會自動更新。
在本系列文章中,如需應用對模塊代碼的修改:
- 添加模型字段時需進行升級。修改 Python 代碼(含 manifest 文件)時需要重啟服務。
- 修改XML或CSV文件時,需進行升級。在不確定時,同時重啟服務並升級模塊。
在不確定時,最保險的方式是通過-u參數來重啟 Odoo 實例,按下鍵盤上、下方向鍵可在使用過的命令間切換。進行這一操作時,我們經常會使用到 Ctrl+C,向上方向鍵和Enter 鍵。
或者要避免這種重復的停止/啟動操作,可使用dev=all選項。這樣在保存XML 和 Python文件修改時會自動進行重載,參見本系列文章第二篇 Odoo 12開發之開發環境准備了解更多詳情。
創建新應用
一些 Odoo 模塊創建新應用,而另一些則對已有應用添加功能或作出修改。雖然兩者的技術組件基本相同,但應用會被預期包含一些特征性元素。我們創建的是一個圖書應用,所以應包含這些元素,它們是:
- 圖標:用於在應用列表中展示
- 頂級菜單項:其下放置所有的應用菜單項
- 應用安全組:通過權限訪問僅對指定用戶開放
添加圖標(icon),僅需在模塊目錄下static/description/子文件夾中放置icon.png文件,前面已經介紹過了。下面我們來添加應用頂級菜單。
添加應用頂級菜單項
我們創建的是一個新應用,因此應包含主菜單項,在社區版本中,顯示在左側下拉菜單中,而在企業版中,則作為附加圖標顯示在應用切換器主界面中。
菜單項是使用 XML 文件中添加的視圖組件,通過創建views/library_menu.xml來定義菜單項:
用戶界面中的菜單項和操作均存儲於數據表中,上面的代碼是一個 Odoo 數據文件,描述了要載入 Odoo 數據庫的記錄。其中的元素是向ir.ui.menu模型寫入記錄的指示。 id 屬性也稱作XML ID,用於唯一標識每個數據元素,以供其它元素引用。例如在添加圖書子菜單時,就需要引用頂級菜單的XML ID,即menu_library。XML ID是一個重要話題,將在本系列文章第五篇Odoo 12開發之導入、導出以及模塊數據中探討。
此處添加的菜單項非常簡單,僅用到了 name 屬性。其它常用的屬性這里沒有使用,沒有設置父菜單,因為這是一個頂級菜單。也沒有設置 action,因菜單項本身並不做任何事,僅僅用於放置后面要創建的子菜單項。模塊還不知道 XML 數據文件的存在,我們需要在__manifest__.py中使用 data 屬性來添加安裝或更新時需要加載的模塊列表以進行聲明。在manifest 文件的字典中加入:
要向Odoo數據庫中加載這些菜單設置,需要升級模塊。此時還不會有什么顯式的效果,因菜單項還不包含可操作子菜單,所以不會顯示。在添加好子菜單及合適的訪問權限時即可顯示。
小貼士:菜單樹中的項目僅在含有可見子菜單項時才會顯示。底層包含窗口操作視圖的菜單項僅當用戶擁有該模型訪問權限時才可見。
添加權限組
普通用戶在使用功能前需獲得相應的權限。Odoo 中使用安全組來實現,權限授予組,組中分配用戶。Odoo 應用通常有兩個組:針對普通用戶的用戶組,包含額外應用配置權限的管理員組。
下面我們就來添加這兩個安全組。權限安全相關的文件通常放在模塊下/security子目錄中,這里我們創建security/library_security.xml 文件來進行權限定義。安全組使用分類來更好地組織關聯應用。所以第一步我們在ir.module.category模型中創建針對圖書應用的分類:
下一步,我們要添加兩個安全組,首先添加用戶組。在以上結束標簽前添加如下 XML 代碼塊:
記錄在res.groups模型中創建,添加了三個字段:
- name:組名
- category_id:關聯應用,這是一個關聯字段,因此使用了 ref 屬性來通過 XML ID 連接已創建的分類
- implied_ids:這是一個one-to-many關聯字段,包含一系列組來對組內用戶生效。這里使用了一個特殊語法,在本系列文章第五篇Odoo 12開發之導入、導出以及模塊數據中會進行介紹。我們使用了編號4來連接基本內部用戶組base.group_user。
然后我們創建管理員組,授予用戶組的所有權限以及為應用管理員保留的其它權限:
像用戶組一樣,這里也有name, category_id和implied_ids ,implied_ids關聯了圖書用戶組,以繼承其權限。還添加了一個 users 字段,讓管理員和內部 root 用戶自動成為應用管理員。
ℹ️在 Odoo老版本中,admin 管理員用戶同時也是 root 用戶。Odoo 12中有一個系統 root用戶,在用戶列表中不顯示,僅在框架需要進行提權(sudo)時在內部使用。admin可以登入系統並應擁有所有功能的訪問權限,但不再像系統 root 用戶那樣可以繞過訪問限制。
同樣需要在聲明文件中添加該 XML 文件:
注意library_security.xml 加在library_menu.xml文件之前,數據文件的加載順序非常重要,因為我們只能引用已經定義過的標識符。菜單項經常引用到安全組,所以建議將安全組定義文件放到菜單和視圖文件之前。
添加自動化測試
編程的最佳實踐包含代碼的自動化測試,對於像 Python 這樣的動態語言尤為重要,因為它沒有編譯這一步,只有在解釋器實際運行代碼時才會報語法錯誤。好的編輯器可以讓我們提前發現問題,但無法像自動化測試這樣幫助我們確定代碼如預期般運行。
Odoo 12中的修改
在老版本中,Odoo 使用YAML文件來進行測試,但 Odoo 12中移除了對YAML文件的支持,所以不能再使用該格式文件。
測試驅動開發(TDD -Test-driven Development) 方法讓我們先寫測試,檢查錯誤,然后開發代碼直至通過測試。受此方法啟示,在添加實際功能前我們先添加模塊測試:
1、測試代碼文件名應以test_開頭,並通過tests/__init__.py引用。但測試目錄(也即 Python 子模塊)不應在模塊的外層__init__.py中引入,因為僅在測試執行時才會自動查找和加載它。
2、測試應放在tests/子目錄中,在tests/__init__.py中添加如下代碼:
3、在tests/test_book.py文件中添加實際的測試代碼:
以上代碼添加一個簡單測試用例,創建一本新書並檢測active 字段的值是否正確。
4、使用–test-enable參數在安裝或升級模塊時進行測試
5、Odoo 服務會在升級的模塊中查找tests/子目錄並運行。現在測試會拋出錯誤,在輸出日志中可看到測試相關的ERROR信息。在為模塊添加完圖書模型后應該就不再報錯。
測試業務邏輯
現在我們應為業務邏輯添加測試了,理想情況下每行代碼都應有一個測試用例。在tests/test_book.py文件test_create() 方法再加幾行代碼:
推薦為每個需檢查的操作添加一個測試用例,本條測試與上一條相似,先創建一本新書。因為各個測試用例是相互獨立的,用例創建或修改的數據會在測試結束時回滾。然后在創建的記錄上調用測試方法來檢查所使用 ISBN是否被正確驗證。
當然,現在運行測試還是會失敗,因為所測試的功能還未被實現。
測試安全權限
也可以對安全權限進行檢測,確定是否對用戶進行了正確的授權。Odoo 中默認測試由不受權限控制的__system__內部用戶執行。所以我們應改變執行測試的用戶,來檢測是否授予了正確的安全權限。這通過在self.env中修改執行環境來實現,只需把 user 屬性修改為希望運行測試的用戶即可。修改tests/test_book.py中的setUp方法如下:
第一條命令調用了父類中的setUp代碼,下面一條修改了用於測試的環境self.env為使用 admin 用戶的新環境。測試代碼的修改到此告一段落。
模型層
既然 Odoo 已經能識別我們的新模塊了,下面就添加一個簡單的模型。模型描述業務對象,如商機、銷售訂單或合作伙伴(用戶、供應商等)。模型中有一系列屬性,也可定義一些特定業務邏輯。
模型通過 Odoo 模板類派生的 Python 類來實現。它直接與數據庫對象對應,Odoo 在安裝或升級模塊時會自動進行處理。框架中負責這部分的是對象關系映射(ORM -Object Relational Mapping)。
我們的模塊是一個圖書管理應用,第一個功能就是管理圖書目錄,目前這是我們唯一需要實現的模型。
創建數據模型
Odoo 開發指南中提到模型的 Python 文件應放在models子目錄中,每個模型有一個對應文件。因此我們在library_app模塊主目錄下創建models/library_book.py文件。
在使用之前,應告知 Python 所需引用的模型目錄,僅需在模塊主__init__.py文件添加:
要引用所創建的 Python 代碼文件,我們還應添加models/__init__.py文件:
現在我們可以在models/library_book.py中加入如下內容:
第一行是 Python 代碼導入語句,讓 Odoo 內核的models和fields對象在這里可用。緊接着聲明了新的模型,它是models.Model派生出的一個類。然后_name 屬性定義了 Odoo 全局對該模型引用的標識符。注意Python 類名 Book 與框架無關,_name 的值才是模型的標識符。
小貼士:僅有模型名使用點號(.) 來分割關鍵字,其它如模塊、XML 標識符、數據表名等都使用下划線(_)。
注意下面的行都有縮進,對 Python 不熟悉的朋友要知道這很重要:相同縮進代表同一代碼塊,所以下面的行應采用相同縮進。
_description屬性不是必須的,但為模型記錄提供了一個用戶友好的名稱,可用作更好的用戶消息。該行之后定義了模型的不同字段 ,值得一提的是name和active為特殊字段名。默認在其它模型中引用模型時,會使用 name 字段作為記錄的標題。
active 字段用於激活記錄,默認僅 active 記錄會顯示。對於日期模型這非常有用,隱藏掉那些用戶在日常操作中不再使用的記錄(因歷史原因仍需保留在數據庫中)。在本項目中,用於標識圖書是否可用。
再來看看其它字段,date_published是一個圖書出版日的日期字段,image 是一個存儲圖書封面的二進制字段。還有一些關聯字段:publisher_id是一個出版公司多對一關聯,author_ids是作者多對多關聯。都是圖書與 partner 模型的關聯,partner 模型內置於 Odoo 框架中,用戶、公司和地址都存儲在這里。我們使用它存儲出版商和作者。
字段就是這些,要使代碼修改生效,需更新模塊來觸發數據庫中相應對象的創建。菜單中還無法訪問這一模型,因為我們還沒有添加。不過可以通過 Technical 菜單來檢查新建模型。訪問 Settings > Technical > Database Structure > Models(需開啟開發者模式),在列表中搜索library.book,然后點擊查看模型定義:
如查看一切順利,說明模型和字段都被正常創建,如果你看不到這些,嘗試重啟服務升級模型。我們還可以看到一些未聲明的字段,這些是 Odoo 自動為新模型添加的保留字段,這些字段有:
- id是模型中每條記錄的唯一數字標識符
- create_date和create_uid分別為記錄創建時間和創建者
- display_name為所使用的記錄提供文本顯示,如其它記錄引用它,它就會被計算並默認使用 name 字段中的文本
- write_date和write_uid分別表示最后修改時間和修改者
- __last_update是一個助手字段 ,它不存儲在數據庫,用於做並發檢測
設置訪問權限
在加載服務時,你可能會注意到輸出日志中有一條警告信息:
The model library.book has no access rules, consider adding one.
提示消息已經很明確了,我們的新模型沒有訪問規則,所以任何人都可使用。我們已為應用添加了安全組,現在就為模塊授權。
ℹ️在 Odoo 12以前,admin 可自動訪問所有數據模型,它是一個不受權限控制的超級用戶。在 Odoo 12中則不再如此,需要在新模型中設置 ACL才對 admin 可見。
添加訪問權限控制
要了解需要哪些信息來為模型添加權限,可訪問后台Settings > Technical > Security > Access Rights:
這里可以看到一些模型的 ACL(Access Control List),表示允許每個安全組對記錄的操作。這一信息需要通過模塊中的數據文件提供,然后載入ir.model.access模型。我們將為 employee 組添加該模型的所有權限,內部用戶是幾乎所有人隸屬的基本權限組。
ℹ️Odoo 12中的修改
User 表單現在有一個用戶類型,僅在開啟開發者模式時顯示。它允許互斥的幾個選項:內部用戶,portal門戶用戶(外部用戶如客戶)和public公共用戶(網站匿名訪客)。這一修改用於避免把內部用戶放到 portal 或 public 組中一類的錯誤配置,那樣會導致權限的喪失。
權限通過security/ir.model.access.csv文件來實現,添加該文件並加入如下內容:
注:應注意該文件第一行后不要留有空格,否則會導致報錯
文件名必須與要載入的模型對應,第一行為列名,CSV 文件中有如下列:
- id是記錄的外部標識符(也稱為XML ID),需在模塊中唯一
- name是描述性標題,僅在保證唯一時提供有用信息
- model_id是賦權模型的外部標識符,模型有ORM自動生成的XML ID,對於library.book,標識符為model_library_book
- group_id指明授權的安全組,我們給前文創建的安全組授權:library_group_user和library_group_manager
- perm_…字段標記read讀, write寫, create創建, 或unlink刪除權限,我們授予普通用戶讀權限、管理員所有權限
還應記得在__manifest__.py的 data 屬性中添加對新文件的引用,修改后如下:
老規矩升級模塊讓修改生效,此時警告信息就不見了。我們通過 admin登錄來檢測權限是否正確,admin 屬於圖書管理員組。
行級權限規則
我們知道默認 active 標記為 False 的記錄不可見,但用戶在需要時可使用過濾器來訪問這些記錄。假設我們不希望普通圖書用戶訪問無效圖書,可通過記錄規則來實現,即定義過濾器來限制某權限組所能訪問的記錄。這位於Settings > Technical > Security > Record Rules。
記錄規則在ir.rule中定義,和往常一樣我們選擇一個唯一名稱。還應獲取操作的模型及使用權限限制的域過濾器。域過濾器使用 Odoo 中常用的元組列表,在第八章 Odoo 12開發之業務邏輯 – 業務流程的支持將講解域表達式語法。
通常,規則應用於指定安全組,我們這里應用的是雇員組。如果沒有指定的安全組,則應用於全局(global 字段自動設為 True)。全局規則不同,它們做的限制非全局規則無法重載。
要添加記錄規則,需編輯security/library_security.xml文件添加如下代碼:
記錄規則位於<data noupdate=”1″>元素中,表示這些記錄在模型安裝時會被創建,但在模型更新時不會被重寫。這么做是允許對規則在后面做自定義但避免在執行模型升級時自定義內容丟失。
小貼士:開發過程noupdate=”1″會帶來麻煩,因為要修復規則時模塊更新不會在數據庫中重寫數據。所以在開發時可以修改為noupdate=”0″來讓數據達到預期結果。
在 groups 字段中,會發現有一個特殊表達式,這是一個帶有特殊語法的one-to-many關聯字段。元組(4, x)表示x應添加到記錄中,此處 x 為一個標記為base.group_user的內部用戶組引用。針對多個字段的這種特殊語法在第六章 Odoo 12開發之模型 – 結構化應用數據中探討。
視圖層
視圖層為用戶界面的描述,視圖用 XML 定義,由網頁客戶端框架生成數據感知的 HTML 視圖。可用菜單項開啟渲染視圖的操作。比如,Users 菜單項處理一個同樣名為 Users 的操作,然后渲染一系列視圖。有多種可用視圖類型,如 list(因歷史原因也稱為 tree)列表視圖和 form表單視圖,以及包含過濾項的右上角搜索框由 search 搜索視圖定義。
Odoo 開發指南寫到定義用戶界面的 XML 文件應放在views/子目錄中。接下我們來創建圖書應用的用戶界面。下面我們會逐步改進並更新模塊來使更改生效。可以使用–dev=all參數來在開發時頻繁的升級。使用該參數,視圖定義會在 XML 文件中直接讀取,無需升級模塊即可在 Odoo 中即刻生效。
小貼士:如果因 XML 錯誤升級失敗,不必驚慌!仔細閱讀輸出日志的錯誤信息,就可以找到問題所在。如果覺得麻煩,注釋掉最近編輯的 XML 版塊或刪除__manifest__.py中 該XML 文件,重新更新,服務應該就可正確啟動了。
添加菜單項
現在有了存儲數據的模型,需要添加到用戶界面中。首先要做的就是添加相應菜單項。編輯views/library_menu.xml文件,在 XML 元素中定義菜單項以及執行的操作:
用戶界面,包括菜單項和操作,存儲在數據表中。在安裝或升級插件模塊時,XML文件會將這些定義載入數據庫中的數據文件。以上代碼是一個 Odoo 數據文件,表示兩條添加到 Odoo 的記錄:
- <act_window>元素定義客戶端窗口操作,它按順序通過啟用列表和表單視圖打開library.book 模型
- <menuitem>定義一個調用前面定義的action_library_book操作的頂級菜單項
現在再次升級模塊來讓修改生效。然后刷新瀏覽器頁面,就可以看到Library頂級菜單,並包含一個子菜單項。點擊該菜單會顯示一個基本列表視圖,記錄可通過一個自動生成的表單視圖進行編輯。點擊 Create 按鈕即可查看:
雖然我們還沒有定義用戶界面視圖,自動生成的列表視圖和表單視圖也可以使用,允許我們馬上編輯數據。
創建表單視圖
所有的視圖都存儲在數據庫ir.ui.view模型中。為模型添加視圖,我們在 XML文件中聲明<record>元素來描述視圖,在模塊安裝時 XML 文件會被載入數據庫。
添加views/book_view.xml文件來定義表單視圖:
這個ir.ui.view記錄有三個字段值:name, model和 arch。另一個重要元素是記錄 id,它定義了一個可在其它記錄中引用的XML ID標識符。這是library.book 模型的視圖,名為Book Form。這個名稱僅用於提供信息,無需唯一,但應易於分辨所引用的記錄。其實可以完全省略 name,這種情況下會自動按模型名和視圖類型來生成。
最重要的字段是arch,它包含了視圖的定義,在 XML 代碼中我們做了高亮顯示(博客主題問題無法顯示)。<form>標簽定義了視圖類型並包含視圖結構。
此處<form>中包含了要在表單中顯示的字段。這些字段會自動使用默認的組件,如 date 字段使用日期選擇組件。有時我們要使用不同的組件,如以上代碼中的author_ids使用了顯示標簽列表的組件,image字段使用處理圖片的相應組件。有關視圖元素的詳細說明請見第十章 Odoo 12開發之后台視圖 – 設計用戶界面。
不要忘記在聲明文件的 data 中加入新建文件,否則我們的模塊將無法識別到並加載該文件:
要使修改載入 Odoo 數據庫就需要更新模塊。需要重新加載頁面來查看修改效果,可以再次點擊菜單項或刷新網頁(大多數瀏覽器中快捷鍵為 F5)。
業務文件表單視圖
上面的部分創建了一個基礎表單視圖,還可以做一些改進。對於文件模型,Odoo 有一個模擬紙張的展示樣式,表單包含兩個元素:<header>來包含操作按鈕和<sheet>來包含數據字段。可以修改上一部分的基礎<form>定義為:
業務邏輯層
業務邏輯層編寫應用的業務規則,如驗證和自動計算。現在我們來為按鈕添加邏輯,通過在模型 Python 類中編寫方法來實現。
添加業務邏輯
上文中我們在 Book表單中添加了一個按鈕,用於檢查 ISBN 是否有效。現代 ISBN 包含13位數字,最后一位是由前12位計算所得的檢查位。我們無需深入到算法的細節,這里是一個實現驗證的 Python 方法。應當在class Book(…)中進行添加:
圖書模型的button_check_isbn()方法應使用該函數來驗證 ISBN 字段中的數字,如果驗證失敗,應向用戶顯示警告信息。
首先要導入 Odoo API庫,添加對應的 import 及 Odoo Warning異常。這需要編輯library_book.py文件修改前兩行為:
然后還是在models/library_book.py文件Book 類中加入:
對於記錄的邏輯,我們使用@api.multi裝飾器。此處 self 表示一個記錄集,然后我們遍歷每一條記錄。其實@api.multi裝飾器可以不寫,因為這是模型方法的默認值。這里保留以示清晰。代碼遍歷所有已選圖書,對於每本書,如果 ISBN 有值,則檢查有效性,若無值,則向用戶拋出一條警告消息。
模型方法無需返回值,但此處需至少返回 True 值。因為不是所有XML-RPC客戶端實現都支持None/Null空值,這種情況若返回空值則會導致拋出錯誤。此時可更新模塊並再次運行測試,添加–test-enable參數來確定測試是否通過。也可以在線測試,進入 Book 表單使用正確和錯誤的 ISBN點擊按鈕進行測試。
網頁和控制器
Odoo 還提供了一個 web 開發框架,可用於開發與后台應用深度集成的功能。第一步我們來創建一個顯示有效圖書列表的簡單網頁。在請求http://<my-server>/library/books頁面時會進行響應,所以/library/books是用於實施的 URL。這里我們簡短地了解下 Odoo 網頁開發,這一話題在第十三章 Odoo 12開發之創建網站前端功能中會深入探討。
Web控制器是負責渲染網頁的組件。控制器是http.Controller中定義的方法,與URL鏈接(endpoint)綁定。 訪問 URL 時執行控制器代碼,生成向用戶展示的 HTML。我們使用 QWeb 模板引擎方便HTML的渲染。
按慣例控制器代碼放在/controllers子目錄中,首先編輯library_app/__init__.py導入控制器模塊目錄:
然后添加library_app/controllers/__init__.py文件來讓目錄可被 Python 導入,並在該文件中添加:
接下來就要創建真實的控制器文件library_app/controllers/main.py,並添加如下代碼:
這里導入的odoo.http模塊,是提供網頁相關功能的核心組件。http.Controller是需要繼承的類控制器,這里在主控制器類中使用。我們選擇的類名和方法並不關聯,@http.route裝飾器才是重要的部分,它聲明了與類方法關聯的 URL 地址,此處為/books。默認訪問 URL 地址要求客戶登錄,推薦明確指出訪問的授權模式,所以這里添加了auth=’user’參數。要允許公開訪問,可為@http.route 添加auth=’public’ 參數。
小貼士:如果使用auth=’public’,控制器代碼中在進行圖書搜索前應使用sudo() 進行提權。這部分在第十三章 Odoo 12開發之創建網站前端功能會進一步討論。
在這個控制器方法中,我們使用http.request.env獲取環境,使用它可從目錄中獲取有效圖書記錄集。最后一步是使用http.request.render() 來處理 library_app.index_template Qweb 模板並生成輸出 HTML。可通過字典向模板傳值,這里傳遞了圖書記錄集。
這時如果重啟 Odoo 服務來重載 Python 代碼,並訪問/library/books會得到一個錯誤日志:ValueError: External ID not found in the system: library_app.book_list_template。這是因為我們還沒有定義模板。下面就一起來定義模板。
QWeb模板是一個視圖類型,應放在/views子目錄下,我們來創建views/book_list_template.xml文件:
<template>元素用於聲明 QWeb 模板,它事實上是一個存儲模塊的 base 模型 – ir.ui.view記錄的快捷方式。模板中包含要使用的 HTML,並使用 Qweb 的特定屬性:t-foreach用於遍歷變量 books的每一項,通過控制器的http.request.render()調用來獲取;t-field用於渲染記錄字段的內容。這里僅簡單地使用 QWeb,更多詳情見第十三章 Odoo 12開發之創建網站前端功能。
在模塊的 manifest 中需要聲明該 XML 文件來供加載和使用。進行模塊升級即可通過http://<my-server>:8069/library/books來訪問有效圖書的簡單列表。
注:以上數據為從 Packt 的 Top 20中添加了當前前3
總結
本文中我們從0開始創建了一個新模塊,了解了模塊中常用的元素:模型、三個基礎視圖類型(表單視圖、列表視圖和搜索視圖)、模型方法中的業務邏輯和訪問權限。我們還學習了訪問權限控制,包括記錄規則以及如何使用網頁控制器和 Qweb 模板來創建網頁。
在學習過程中,我們熟悉了模塊開發過程,包含模塊升級和應用服務重啟來使得修改在 Odoo 中生效。不要忘記在添加模塊字段時需要進行更新操作。修改含聲明在內的 Python 文件需要重啟服務。修改XML或CSV文件需進行更新,一旦不確定,同時進行重啟服務和升級模塊操作。
我們已經學習創建 Odoo 應用的基本元素和步驟,但大多數情況下,我們的模塊都是對已有應用添加功能來進行擴展,我們將在下一篇文章中一起學習。
☞☞☞第四章 Odoo 12 開發之模塊繼承
學霸專區
- library-app是正確的模塊名嗎?
- 模塊是否應為其中所有的模型定義訪問控制列表(ACL)?
- 是否可以讓某些用戶僅訪問一個模型記錄的子集?
- 關聯字段和其它字段類型有什么區別?
- Odoo 應用中使用的主要視圖組件有哪些?
- 后台視圖如何定義?
- Odoo 應用中的業務邏輯應在哪里實現?
- Odoo 使用的網頁模板引擎是什么?