大家好,
Odoo 提供了一個快速的應用程式開發框架,特別適用於建構商務應用程式。這類型的應用程式通常主要用於保存商務記錄,主要集中在創建,讀取,更新和刪除(CRUD)操作。 Odoo 不僅可以輕鬆建構這種類型的應用程式,還提供了豐富的元件來建立使用者界面,例如看板和日曆視圖。
首先說明 Odoo 框架的概念,設置開發環境以及建構一個簡單的 Odoo 應用程式。建構了 Odoo 的應用程式之後,就需要對程式進行測試,部署到正式環境並進行維護。
在本篇文章中,我們將很快進入操作,在設置本地開發環境之前,會先從 Web 使用者界面探索 Odoo 內部。
了解 Odoo 架構,了解每個元件的適用位置,熟悉主要的 Odoo 元件之後,未來將更詳細地介紹三個主要應用層:模型,商務邏輯和視圖。然後透過範例使用開發人員模式建立一個簡單的 Odoo 應用程式,學習如何向現有模型加入欄位,加入新模型,建立自定視圖,並透過選單項將其提供給使用者。
接下來我們將說明 Odoo 開發的概念,然後再使用開發人員模式執行自定操作。
Odoo 架構
Odoo 遵循多層軟體架構,有三個主要層次:資料,邏輯和表示:
- 資料層 (Data tier) 是最底層,負責資料儲存。 Odoo 使用 PostgreSQL。 PostgreSQL 是支援 RDBMS 的資料庫程式;目前 Odoo 只支援 PostgreSQL,不支援 MySQL 等其他資料庫。而二進制檔案,例如檔案或圖片的附件通常儲存在檔案系統中。
- 邏輯層 (Logic tier) 負責與資料層的溝通,並由 Odoo 核心處理存取般而言,底層資料庫只能由該層存取,因為它是確保安全存取控制和資料一致性的唯一方法。在 Odoo 的核心中,有設計物件關聯對映 (ORM) 引擎。 ORM 提供 addon 模組使用應用程式介面 (API) 與資料進行溝通。
例如,Partner (如客戶或供應商) 在 ORM 中由模型表示。此模型是一種 Python 物件類別,支援多種溝通方法,例如 create() 可以建立新的 Partner 記錄,或者使用 read() 來查詢記錄及資料。這些通用方法可以在特定模型中實作特定的商務邏輯。例如,create() 方法可能實作預設值或強制驗證規則;read() 方法可以支援一些自動計算的欄位,或者根據執行該操作的使用者控制他的存取權限。 - 表示層 (Presentation tier) 負責呈現資料並與使用者進行溝通。客戶端與 ORM API 進行溝通以讀取,寫入,驗證或執行任何其他操作,透過遠端程序呼叫 (RPC) 去呼叫 ORM API 的 method。 將這些操作發送到 Odoo 進行處理,然後將結果發送回客戶端以供進一步處理。對於表示層,Odoo 提供了一個全功能的 Web 客戶端。 Web 客戶端支援商務應用程式所需的所有功能:登錄,導航選單,資料列表,表單等。雖然外觀不像前端開發人員所期望的那樣具有彈性,但可以輕鬆建立功能一致的使用者體驗。互補的表示層是包含的網站框架。它提供了完全的靈活性,可以像建立其他 CMS 框架一樣精確的使用者界面來建立網頁,但需要付出一些額外的努力和 Web 專業知識。網站框架支援 Web 控制器實作代碼來呈現特定的邏輯,與模型內在的邏輯分開。前端開發人員可能會在這個地方感覺比較親切。
由於 Odoo 服務器 API 的開放性,可以在其他客戶端實作,並且可以在其他平台或程式語言建構或溝通。可以建構桌面和智慧手機應用程式,以提供特定的使用者界面,利用 Odoo 資料和邏輯層來實作商務邏輯和資料。第三方 Odoo 客戶端的一個例子是 ERPpeek。
它是一個命令行客戶端,可以連接遠端 Odoo 並進行溝通。對於具有 Odoo 技術知識的開發人員或系統管理員而言,它可以用於檢查 Server 上的資料,腳本進階操作或執行維護操作。
開發者模式
許多 Odoo 客制可以直接從使用者界面進行。這是一種快速的修改方式,例如加入一個欄位到自定設置,或使用多個模型,視圖和選單項來建立應用程式。
與寫程式的方式來客制化相比,直接從使用者界面去客制會有一些限制。例如,您不能加入或擴展 ORM 方法。而且也不容易導入到結構化開發工作流程中,例如執行自動化測試並部署到多個環境中。
以後會再慢慢介紹 Odoo 開發,用程式建立 addon 模組。
本篇會先使用開發者模式直接從 Web 客戶端的使用者界面實作客制,因為對於 Odoo 開發人員來說,了解這些工具以及他們可以實作什麼是非常重要的。所以下面練習的目標是去理解 Odoo 應用程式中圖層的概念以及它們的元件的組織方式。
建立一個簡單的圖書館程式
接下來將會建立一個非常簡單的圖書館應用程式。圖書館會記錄書籍以及作者,所以會需要兩種模型:作者和書籍。這兩個模型之間有多對多的關係:
- 每個作者可以有很多書。
- 每本書可以有很多作者。
Odoo 已經提供了內建的 Partner 模型和 res.partner 來代表人員,組織和地址。例如,客戶,供應商,聯絡人和應用程式使用者都是屬於 Partner。我們可以很簡單地將 Partner 當作圖書館應用程式中的作者模型,另外再加入 Is Book Author? 欄位。另外再建立一個新的書籍模型,有相應的表單 (form) 和列表 (list) 視圖,以及一個存取它們的選單項。
建立一個工作資料庫
我們需要一個 Odoo 的測試資料庫。將來會介紹如何從 source code 安裝 Odoo,並設置開發環境。但現在先跳過這一步,直接從現有的 Odoo 使用者界面來操作。在登錄的畫面上,存取"管理資料庫"選單來建立一個新的資料庫。
如果本機端沒有安裝 Odoo,則可以在 Odoo.com 上建立一個測試的資料庫。Odoo Enterprise Edition 的使用者界面與社區版畫面有些不同。要建立一個新的資料庫,可能會被要求選擇一個開始的應用程式。如果不確定要選擇什麼,就選 CRM 吧。
Odoo 採用"開放核心"的商業模式,產品以社區和企業兩種版本發布。企業版建立在社區版之上,增加了一些進階功能,包括改進的使用者界面。在 Odoo.com 上,是使用企業版。雖然這兩個版本的使用者界面看起來不同,但它們只是外觀作的改變;使用者界面功能在兩個版本中都是相同的。現在,使用瀏覽器存取 Odoo 資料庫並登錄。Odoo 的線上版,網址應該類似 https://<mydbname>.odoo.com。實例是似於 http://<server-address>:8069。8069 Port 是 Odoo 的預設 Port。
我們將使用聯絡人目錄應用程式,因此首先要做的就是安裝它。如果您尚未這樣做,請打開應用程式頂部選單,搜尋此應用程式並進行安裝。
現在,我們有一個 Odoo 可以測試了,下一步是啟用開發人員工具,在 Odoo 內有較高的存取權限。
啟用開發者工具
在 Settings | Dashboard 中的右下角,可以找到 Activate the developer mode 連結。點擊它可以啟用開發者模式。
另外有一個 Activate the developer mode (with assets) 的選項。它所做的是防止 Web 客戶端資源壓縮。對於除錯 Web 客戶端本身很有用,但會導致速度稍慢。為了加快載入時間,Web 客戶端將 Javascript 和 CSS 資源縮減為精簡檔案。但會使 Web 客戶端無法除錯。Activate the developer mode (with assets) 選項可防止壓縮並將 Web 資源載入到單個非壓縮檔案中。
您可以啟用開發人員模式,而無需透過連結打開設置。只需編輯當前的 URL 即可在 /web 部件後面插入 ?debug 或 ?debug=assets。例如,http://myserver/web#home 會更改為http://myserver/web?debug#home,雖然前端框架沒有連結可以啟用開發者工具,但將 debug=assets 加入到相應的 URL 就可以啟用了。
啟用開發者模式後,我們會看到另外兩個選單:
- Debug 選單,頂部選單欄右側的蟲蟲圖示,位於使用者名和頭像之前
- Technical 選單項位於設置應用程式:
開發者模式可以讓滑鼠游標停留在欄位上時,工具提示會顯示訊息。
我們將在下一部分中解釋和使用這些開發人員模式功能。
在現有的模型中加入欄位
將自定欄位加入到現有表單是一種常見的方式,可以從使用者介面完成,不需要建立 module 或 model。對於我們的圖書館應用程式,我們想要加入 Is Book Author? 欄位到 Partner 模型,以便更容易地列出書籍的作者。
請在 Settings 選項中選到 Technical | Database Structure | Models 選單,並搜尋 res.partner 模型,以及 Model Description 為 Contact。
點擊它打開相應的表單視圖,您將看到關於 Partner 模型的所有具體細節,包括欄位列表:
現在,Edit 表單並點擊欄位列表底部的 Add an item。彈出窗口將顯示新的欄位建立。讓我們填寫配置:
- 欄位名稱:x_is_book_author
- 欄位標籤:Is Book Author?
- 欄位類型:boolean
欄位名稱必須以 x_ 開頭。這對透過使用者介面直接建立的模型和欄位是必需的;但透過 addon 模組進行的客制則沒有限制。
點選 Save & Close,我們的新欄位應該已加入到欄位列表中。有可能這個模型有超過 8 個欄位,因此您可能需要導航到欄位列表的下一頁來查看它。請使用欄位列表左上角的右箭頭。現在,點擊左上角的 Save 按鈕。我們的新欄位現已在 Partner 模型中,但使用者還看不見。
因此還需要將其加入到視圖。不過,在 Partner/Contact 模型表單上,選擇 Views,我們將能夠看到 res.partner 模型的所有視圖定義。如您所見,每個視圖都是資料庫中的記錄。更改或加入查看記錄會立即生效,並在下次重新載入 View 時顯示。
在視圖列表中有幾件重要的事情需要注意。我們可以看到有幾種視圖類型,例如 Form,Tree,Search 或 Kanban。
Tree 和 List 都可以用來表示相同的視圖類型。它們實際上是列表,只是歷史原因存在 Tree 名稱:在以前的列表視圖中,有一個 Tree 的分層模式。
如果您對 View Type 進行排序,您會注意到相同 View Type 可以有多個定義。實際上,我們可以有好幾個基本視圖定義。我們可以擴展視圖定義,也稱為繼承視圖,可以增加變更到基本視圖中,例如,將欄位加入到現有表單。
對於 res.partner 模型,我們可以看到有兩種基本形式:res.partner.form 和 res.partner.simplified.form。
Client Actions (例如選單項中的操作)可以指定要使用的基本視圖。如果沒有定義,則使用具有最低順序的視圖。您可以將其視為預設視圖。點擊 Views,我們將看到一個包含 View 的細節的表單,包括 Sequence 值。
要了解使用者界面中某處使用的視圖,我們可以使用 Debug 選單對其進行檢查。現在我們來試試吧。點擊聯絡人 (Contacts) 應用程式,我們將看到許多聯絡人卡片列表。點擊任何卡片都會顯示相應的 Form View。
現在,在 Debug 選單(右上角的蟲蟲圖示)中,選擇 Edit Form View 選項:它會顯示與之前在 Models 中看到的視圖詳細訊息。正如你所看到的,它是 res.partner.form 視圖。
在 Architecture 中,我們可以使用視圖定義查看 XML。我們可以編輯它來加入我們的新欄位。儘管如此,但從長遠來看,這不是一個好主意。這個視圖由一個 addon 模組所擁有,如果將來某個時間模組被升級,這些客制將被覆蓋並消失。我們可以透過外部 ID 欄位了解所有者模組。在這種情況下,它是 base.view_partner_form,所以我們知道這個視圖是屬於 base 模組。
修改視圖的正確方法是建立一個繼承視圖擴展。
首先,我們需要從原始視圖中選擇一個元素作為擴展點。我們可以透過檢查基本視圖並選擇一個具有 name 屬性的 XML 元素來完成此操作。 通常是一個 <field> 元素。在這裡選擇 <field name =‘category_id’…> 元素。打開 Debug 選單,單擊 Edit Form View 選項,選擇 Inherited Views 選項卡,然後點擊列表底部的 Add a item。將會顯示一個彈出視窗:Create Views which inherit from this one:
我們應該填入下面的值:
- View Name: Contacts - Custom Is Book Author Flag
- Architecture: Use the following XML
<field name="category_id" position="after"> <field name="x_is_book_author" /> </field>
其他重要欄位(如模型,視圖類型和繼承視圖)已具有正確的預設值。
我們現在可以 Save & Close。Save 在編輯窗體視圖窗口中,然後關閉它。重新載入聯絡人視圖後,我們將能夠看到所做的更改。(按 F5 重新載入頁面看看結果吧!)
加入選單,模型以及視圖
現在我們將建立新的應用程式功能,而不是擴展現有的功能。我們將繼續增加圖書館功能。
首先,我們要建立一個圖書館頂層選單,然後建立 Authors 的子選單項以及 Books 子選單項。接下來,我們將建立一個新的書籍模型,並透過選單項提供使用者使用。最後,我們將為 Books 模型建立列表 (list) 和表單 (form) 視圖。
建立選單
我們現在可以透過 Is Book Author? 被勾選的所有 Partners,將作者列表出來.
現在要加入一個 Authors 選單項,打開該列表,過濾掉其他非作者的 Partner。Odoo 的優點是可以透過重用已有的 Partner 視圖來輕鬆完成此操作。在此之前,先幫圖書館應用程式建立頂層選單,在這個選單項目下可以包含 Authors 的子選單項以及 Books 子選單項。
選單定義在 Settings 應用程式的 Technical | User Interface | Menu Items。
使用以下的數值建立一新選單項:
- Menu:Library
- Parent Menu:(empty)
- Action:(empty)
在 Submenus 的頁籤中,單擊加入項目並填入值加入子選單:
- Menu:Book Authors
- Parent Menu:Library(預設值)
- Action:選擇 ir.actions.act_window,並在選擇列表中右鍵單擊 Create and Edit,打開一個表單來建立相關的 Window Action。
在窗口操作中設置以下值:
- Action name: Book Authors
- External ID Object: res.partner
重新載入 Web 客戶端。我們可以看到選單是一個選單項目的樹,具有父/子關係。子選單項有一個相關的 Action,定義了它被選中時會發生什麼。這個 Action 名稱將被用作所呈現視圖的標題。
有幾種常用的 Actions 種類,最重要的是 Window, Reports, 和 Server Actions。Window Actions 是最常見的,用於在 Web 客戶端中顯示視圖。Report Actions 用於執行報告,Server Actions 用於定義自動化任務。
我們剛剛建立的選單項 Book Authors 使用一個 Window Actions,它可以直接從 Menu Item 的表單建立。我們也可以從 Settings | Technical | Actions 選單選項中建立。但我們建立的 Actions 太簡單了,並且沒有達到我們想要的效果:它會開啟一個包含所有 Partners 的列表,無論 Is Book Author? 有沒有被選擇到,我們需要解決這個問題。
打開 Window Actions 選單項,看看剛建立的 Book Authors,然後編輯它。我們對 General Settings 頁籤內的 Filters 感興趣。
在很多情況下,使用 Debug 選單中的 Edit Action 選項會更方便,它存取了一個方便的快捷方式來編輯用於存取當前視圖的 Window Action。
Domain Value 欄位可以有一個表達式,用於定義要呈現的記錄的過濾器。這個 “domain expression” 遵循 Odoo 特有的語法,將在未來的文章中解釋。現在,知道它是一個元組列表就足夠了,每個元組都是一個過濾條件。
在我們的例子中,用於 Domain Value 的表達式是:
[('x_is_book_author', '=', True)]
為了可用性,我們還希望建立新記錄時 Is Book Author? 預設是 enable 的。使用 Context Value 可以做到這一點,允許我們在它所呈現的視圖上設置預設值。Context 是 Odoo 傳遞 session information 的方式,包括要使用的預設值。它將在以後會詳細討論,現在我們只需要知道它是一個字典型態。以 default_ 為前綴的值提供相應欄位的預設值。
在我們的例子中,Context Value 所需的表達式是:
{'default_x_is_book_author': True}
就是這樣。如果我們現在嘗試 Library | Book Authors 選單選項,它應該只列出 Is Book Author? 有被選擇的 Partners。如果我們嘗試建立新的書籍作者,我們會看到 Is Book Author? 複選框預設的情況下是 enable 的。
建立模型
現在有一個圖書館頂層選單包含了 Book Authors 子選單, 可以再加入一個 Books 子選單項來存取書籍。
讓我們再次存取 Settings | Technical | Database Structure | Models,然後點擊建立並填寫模型表單:
- Model Description: Book
- Model: x_library_book
先保存它,然後才能向其加入新欄位。因此,點擊保存,然後再次編輯。您可以看到幾個欄位被自動加入。
x_name 欄位是標題,或者是在其他記錄中引用它的標題。您可以編輯它並將欄位標籤更改為更有意義的標題。我們還要幫 book authors 加入一個欄位。一本書可以有很多作者,作者都可以有很多書。所以這是一個多對多的關係。
在欄位列表中,單擊加入項目以使用以下值建立新欄位:
- Field Name: x_author_ids
- Field Label: Authors
- Field Type: many2many
- Object Relation: res.partner
- Domain: [(‘x_is_book_author’, ‘=’, True)]
many-to-many 欄位有幾個特別的定義:關係表 (Relation Table),行 1 和行 2 欄位。這些會自動填寫好,大多數情況下預設值都很好,所以我們現在不需要擔心它們。Domain 的屬性則是讓 book authors 可以從列表中選擇。否則會導致所有 Partners 都可以選擇。
我們現在需要在使用者界面中提供該模型。如果我們為新模型建立一個選單項,它可以立即使用,因為 Odoo 會自動為它產生預設視圖。
在 Settings | Technical | User Interface | Menu Items,使用以下值建立新記錄:
- Menu: Books
- Parent Menu: Library
- Action: ir.actions.act_window,並在選擇列表中右鍵單擊 Create and Edit,打開一個表單來建立相關的 Window Action:
- Action Name: Books (將是用於所呈現的視圖的標題)
- External ID Object: x_library_book (目標模型的技術名稱)
試試看,你會看到基本功能,自動產生的視圖。我們希望創造我們自己的視圖,這就是我們將在下一節中討論的內容。
建立視圖
我們已經建立了 Books 模型,並透過選單項在使用者界面中提供。接下來,我們將為它建立兩個重要視圖:一個列表(也稱為 tree)和一個表單 (form)。
在 Settings | Technial | User Interface | Views 建立一個新紀錄並包含下面的值:
- View Name: Books List View
- View Type: Tree
- Model: x_library_book
在 Architecture 頁籤中, 寫入以下的 XML code:
<tree> <field name="x_name" /> <field name="x_author_ids" widget="many2many_tags" /> </tree>
列表視圖的基本結構非常簡單:<tree> 包含一行或多行要在列表視圖中顯示的 <field> 的元素。我們在作者欄位中加入了小元件屬性,使其呈現為按鈕式標籤。
接下來,我們將建立表單視圖。建立另一個新記錄,並使用以下值:
- View Name: Books Form View
- View Type: Form
- Model: x_library_book
在 Architecture 頁籤中, 寫入以下的 XML code:
<form> <group> <field name="x_name" /> <field name="x_author_ids" widget="many2many_tags" context="{'default_x_is_book_author': True}" /> </group> </form>
表單視圖結構有一個根元素 <form>,其中包含像 <field> 等元素。在這裡,我們還為作者欄位選擇了一個特定的小元件,以顯示為標籤按鈕而不是列表網格。我們還加入了一個 Context 屬性,從此處直接建立新的作者時,它們將啟用 Is Book Author? 複選框。
設定安全的存取權限
Odoo 包含內建的存取控制機制。使用者將只能使用他有權限存取的功能。這意味著我們存取的圖書館功能不能被使用者存取。
管理員是一個特殊情況;存取控制機制不適用於它。
存取的基本控制來自於 Groups。安全的 Group 有權限存取模型,或是可以限制更細微的權限控制,我們還可以存取特定的選單項,視圖,欄位,甚至資料記錄(使用記錄規則)。
通常每個應用程式至少提供兩個組:
- User,作能夠執行日常任務。
- Manager,能夠執行該應用程式的所有配置。
讓我們建立一個新的安全 Groups。在 Settings | Users & Companies | Groups。使用以下值建立新記錄:
- Application: 輸入 Library,然後在彈出窗口中選擇建立 “Library” 選項
- Name: User
- Inherited tab: 加入 Employees / Employee:
圖書館應用程式在應用程式選擇列表中還無法使用,所以我們直接從 Group 表單中加入它。我們也讓它"繼承"了 Employee Group。表示該組的成員也將成為繼承的成員,而有效的授予所有成員的權限。Employee 是基本存取的 Group,應用程式 security Groups 通常會繼承它。現在我們可以將特定模型的存取權限授予 Groups / Library / User。我們可以使用 Groups 表單的 Access Rights 頁籤。在這裡加入一個項目,使用這些值:
- Object: select Library Book from the list
- Read, Write, Create and Delete Access: Checked
- Name: Library Book User Access
Name 屬性只是提供訊息,但是是強制性的。模型存取也可以透過 Technical | Security | Access control List 選單項存取,我們不需要加入對 Partner 模型的存取權限,因為我們繼承了權限存取它的員工組。
我們現在可以在使用者上嘗試這個新的安全 Group。如果您正在使用安裝了 Demo 資料的 Odoo 實例,則應該有我們可以使用的 Demo User。如果不是,您可以建立或使用現有的使用者。重點是不要使用管理員,因為它具有特殊的安全特權並繞過存取控制。
選擇 Users & Companies | Users 選單項並編輯 Demo User 表單:
在 Access Rights 頁籤,我們應該可以看到一個圖書館選項,我們可以在其中選擇 User,儲存後從管理員帳戶 logout,並用 demo 當作使用者 login (預設密碼是 demo)。
如果一切正常完成,您應該能夠看到圖書館頂層選單,並用它來加入書籍和作者。
關於 Odoo 的基礎模型
在本章中,我們有機會建立新的 Books Model,同時也利用已有的 Partners Model。這些開箱即用的模型值得進一步了解。
在 Odoo 的核心,我們有基本的插件模組。這提供了 Odoo 應用程式所需的基本功能。然後,我們有一組內置的插件模組,提供標準產品提供的官方應用程式和功能。基礎模組提供兩種模型:
- Information Repository, ir.* models
- Resources, res.* models
資訊儲存庫 (Information Repository) 用於儲存 Odoo 需要知道如何作為應用程式工作的 data,例如選單,視圖,模型,操作等。
我們可以在 Technical 選單中找到的 data 通常儲存在資訊儲存庫模型中。一些相關的例子是:
- ir.action.act_window = Windows Actions
- ir.ui.menu = Menu Items
- ir.ui.view = Views
- ir.model = Models
- ir.model.fields = Model Fields
資源 (Resources) 則包含有關商務的基本資料,這對許多應用程式都很有用。這些是一些相關的資源模型:
- res.partner: for business partners, such as customers, suppliers, and so on
- res.company: for company data
- res.currency: for currencies
- res.country: for countries
- res.users: for application users
- res.groups: for application security groups
這些資訊可以在將來遇到模型時更好地了解模型的來源。
總結
在這篇文章中,我們不僅概念了 Odoo 元件的組織結構,還利用開發者模式深入研究了 Odoo 內部元件,並理解這些元件如何一起建立應用程式。我們使用這些工具建構了一個簡單的應用程式,包括模型,視圖和相應的選單。我們還了解到開發人員工具可用於檢查現有應用程式或直接從使用者界面快速進行自定。
在下一章中,我們將開始更加認真地了解 Odoo 開發,並將學習建立和組織我們的開發環境。
參考資料
- Daniel Reis. Odoo 11 Development Essentials. Third Edition.