Odoo快速入門與實踐 | 技術研發篇 | 自建應用入門
自建應用入門
github地址
https://github.com/rowrycho/Odoo_bug_manage
講解視訊地址
還在錄製...
Odoo快速入門與實踐 | 技術研發篇 | 目錄
https://www.cnblogs.com/Rowry/p/13348205.html
目錄
目錄
在Odoo的開發過程中,建立自己的模組是非常重要的
-
前面已經提到過: 不建議直接修改原始碼.
-
所以即便是修改已有的模組,也最好是新建一個模組,然後繼承已有的模型進行擴充套件增強.
在admin使用者的登入介面裡,為什麼設定前面的選單稱為應用而不是叫做模組呢?
- 這是因為模組和應用本身就是不同的,應用是一個獨立的系統,比如HR,CRM等,而模組這只是一組較為獨立的功能,比如HR(應用)的招聘模組.
- 雖然招聘這個功能較為獨立,但是很少有公司使用一個只有招聘功能的系統,但卻有很多公司使用HR系統,這就是應用和模組的區別.
- 一般來講,應用在Odoo裡面有一個頂級選單,而很多模組只是在這些頂級選單的下面增加的子選單或動作,這也是本書經常將模組稱為外掛模組
在本章中,我們將建立自己的第一個應用,並且在這個應用過程中介紹Odoo自建應用的主要流程和節點.
對於本次的專案,我們先做一個bug管理系統,通過Odoo來完成一個bug提交,bug狀態修改等功能的模組,希望能夠以此瞭解建立自建模組的步驟.
前面已經介紹過,Odoo的整體架構是MVC分層設計的,所以在實現bug管理系統的過程中,我們會接觸到model層,view層,controller層,然後再設定本專案的訪問控制器.
完成本章後,我們能夠了解Odoo的基本構造,並可以重新開始體驗一個專案的過程.
使用腳手架建立新模組
將一組獨立的功能按照不同的模組外掛進行設計
- 要了解一個模組的結構,可以通過其檔案
__manifest__.py
來了解,該檔案中列出了包含哪些檢視,模型和演示資料,並且還可以配置本模組說明等基本資訊,每個模組都必須包含這個檔案.
前文中已經提到過,不建議直接修改已有的程式碼,而是需要自建一個新的模組.
-
我們只要自己配置好一個第三方的模組存放路徑,並在 odoo.conf(配置檔案)中進行宣告
-
使用如下命令建立模組:
python odoo-bin scaffold bug_manage myaddons
-
下面來看看該模組的檔案結構:
-
此時開啟
__manifest__.py
檔案,可以看到裡面是以字典形式存放的資料(一個{}),下面我們對檔案內各個關鍵字(或者稱為引數)的意義進行解釋 -
我們根據自己的需求,只調整調整前面的幾個引數即可,其他使用檔案給出的預設值即可.
安裝和更新模組
我們已經建立了自己的模組,雖然沒有進行業務邏輯和檢視的開發,但是現在已經可以安裝到Odoo了.
- 在初期瞭解Odoo的階段,建議各位讀者每進行一小步都及時進行測試,這樣便於及時發現問題(測試不一定是"高大上"的,直接點選檢視效果測試,或者直接輸出print(),就是最簡單粗暴的但是非常有效的測試,更加專業的測試應該個測試工程師去做)
所以本節首先介紹如何安裝模組以及如何升級,並且介紹伺服器的開發模型.
安裝模組是在前端完成的,我們使用admin使用者登入Odoo系統並按照以下步驟進行操作:
- 點選 設定 介面右下角的 啟用開發者模式 進入開發者模式.
- 在 應用 介面點選左側的 更新應用列表 按鈕, 在彈出的對話方塊中點選 更新 按鈕.
- 然後搜尋框中搜索bug,進行安裝
如果後續對本模組進行了修改,那麼可以繼續使用該方法,只是到時候圖表右下角會出現升級按鈕,點選升級即可,也是要先更新應用列表.
因為Odoo服務只在啟動的時候對Python程式碼載入過一次,所以如果修改了Python程式碼,則需要重新啟動Odoo.
- 更為安全的方式時在重啟服務的時候,加上更新的引數:
python odoo-bin -d 資料庫名字 -u 要更新模組的名稱
- 當然,如果把
-u
替換為-i
,這樣就可以安裝模組了,對於Odoo11來說,還是在前端頁面安裝給更為方便一些.
注意: 如果希望在修改了Python程式碼之後不重啟伺服器就能讓修改起作用,那麼可以使用Odoo的伺服器開發模式,只需要加上一個引數 -dev=all
即可
- 在伺服器開發模式下,每當我們儲存Python檔案時,伺服器都會重新載入程式碼,這樣就極大的加快了開發週期(其實不是很推薦)
下面我們通過一個小例子來檢視升級的功能,我們為bug管理應用新增一個圖示
-
我們需要
bug_manage
專案下建立目錄\static\description
,並將圖示命名為icon.png
-
點選 更新應用列表,既可以看到這個新增的圖示
模型
我們已經在Odoo安裝了自己的模組(module),現在可以新增自己的模型(models)了.
- 模型可用於描述業務物件,如訂單,憑證等. (所有的物件都是可以使用欄位來抽象資訊化表示的)
- 模型是通過繼承Odoo模板的Python類(odoo.models.Model)進行增強擴充套件來實現的.
- 建立模型後,Odoo會通過ORM引擎在資料庫自動建立表.
我們的模型要完成的任務也比較簡單,只是去維護bug列表.
- 每個bug均包含描述資訊和是否完成的標示
- 後續將再來慢慢新增一些bug關閉等功能
建立模型
我們在models
資料夾下建立bugs.py
檔案,然後在裡面編輯程式碼,如下:
from odoo import fields, models, api
class Bugs(models.Model):
_name = "bm.bug"
_description = "bug"
name = fields.Char(string='bug簡述', required=True)
detail = fields.Text(size=150)
is_closed = fields.Boolean(string="是否關閉")
close_reason = fields.Selection([("changed", "已修改"), ("cannot", "無法修改"), ("delay", "推遲"), ],
string="關閉理由")
user_id = fields.Many2one('res.users', string="負責人")
follower_ids = fields.Many2many('res.partner', string='關注者')
關於模型的定義會在後面介紹,暫時可以將其看成是欄位.
常用屬性
從上面的例子中,我們可以看到,欄位裡面有多種屬性,此處統一對常用的屬性做一些介紹
- string: 在前端介面看到的欄位名稱,預設是欄位的技術名稱(技術名稱就是
__manifest__.py
中的name). - required: 預設是False,如果設定成True,則在建立記錄時該欄位不允許為空.(只是在前端頁面的限制)
- help: 在前端使用時作為提示資訊. (滑鼠懸停的時候會給出提示資訊)
- index: 布林型別,預設為False. 如果是True,則會在資料庫的該欄位上建立索引.
保留欄位
在模型裡面,有一些欄位是系統保留的,作為開發人員不能修改這些欄位,這些保留欄位具體如下:(這些欄位是在模型定義的沒有定義的,但是Odoo會建立表的時候自動加上,所以是客觀存在的)
- id: 這是記錄的唯一標識
- create_date: 記錄建立的日期
- create_uid: Many2one型別,建立該記錄的使用者
- write_date: 記錄的最後修改日期
- write_uid: Many2one型別,記錄的最後修改使用者
- _last_update: 該欄位並不會實際儲存值,在這裡僅起到併發檢查的作用
如果要檢視以上欄位的具體值,
- 可以在前端的開發者模式下,通過debug(臭蟲按鈕)的檢視元資料選項檢視資料明細
- 也可以直接在資料庫中檢視 bm_bug 資料表
注意: name欄位是模型裡面的特殊欄位,在預設情況下該欄位的值可用來在搜尋和引用時代表一行記錄.
模型繼承
前面的章節,我們已經通過新建模型建立了Bugs模型. 在我們的引用中還有一個與bug是many2many關係的關注者,關注者就是關注bug的人,我們準備繼承res.partner
進行修改.
前面新建的模型是通過Python類來完成的,擴充套件和增強已有模型也是通過Python類來完成的.
Odoo提供了三種模型的繼承機制,具體如下:
-
經典繼承: 允許子類修改父類定義的方法,欄位,也允許新增欄位和方法.
- 只需要使用
_inherit
,預設_name
欄位與繼承的模型一致(也就保證了是同一張資料表)
- 只需要使用
-
原型繼承: 和經典繼承一樣,只不過資料表是新建了一張資料表.
- 需要使用
_inherit
和_name
,_name
和繼承的模型_name
不一致(所以在資料庫中會複製原來的內容,在新建一張資料表) - 不推薦使用這種方式,因為會造成資料的冗餘
- 需要使用
-
委派: 將子類中新增的欄位與父類中的欄位進行關聯,相當於保留了父類的欄位和方法.
- 需要一個"外來鍵欄位", 只需要使用
_inherits
(注意: 後面有s)
- 需要一個"外來鍵欄位", 只需要使用
我們選擇經典繼承方式來繼承使用res.partner
類,仍然是在models檔案下建立類followers.py
,並且編輯程式碼:
from odoo import models, field, api
class Follower(models.Model):
_inherit = 'res.partner' # 繼承欄位,宣告要繼承類的_name
bug_ids = field.Many2many('bm.bug', string="bug") # 新增欄位
我們建立了兩個類,但是不要忘記在 models/__init__.py
檔案中引入這兩個新檔案.
- 這是Python語法習慣的要求,如果不在此處,則Odoo將無法發現這兩個檔案.
- 我們應該在
__init__.py
檔案內增加如下程式碼
from . import bugs
from . import follower
注意: 應該修改了內建的res.partner
,所以重新啟動Odoo伺服器會報錯
- 解決方法:
python odoo-bin -c odoo.conf -u bug_manage
啟動時候加上-u 要更新的模組
檢視
前面已經建立了模型,我們還需要前端介面,本節就來介紹檢視.
- 注意: 在還沒有配置檢視時候,安裝了應用在前端是不會顯示的
- 如果都是使用預設的tree,search,form,那麼配置一個menuitem和action即可
檢視是通過XML檔案來定義的,前端框架(QWeb)會解析XML檔案然後生成HTML檔案提供給瀏覽器執行.
檢視的基本元素:
- 選單(menuitems)
- 動作(action)
- 列表檢視(tree)
- 搜尋檢視(search)
- 表單檢視(form)
我們通過後臺的views
資料夾下的XML檔案來建立前端檢視. 現在,我們就為bug管理系統應用建立檢視.
新增選單
我們先來建立bug管理系統的使用者介面,需要在views
資料夾下新建檔案bugs.xml
,並編輯如下程式碼
<odoo>
<data>
<!--動作視窗可以理解為一個橋樑 檢視 <= 動作 => 選單-->
<record model="ir.actions.act_window" id="bug_manage.action_window">
<field name="name">bug_manage window</field>
<field name="res_model">bm.bug</field>
<field name="view_mode">tree,form</field>
</record>
<!--頂級選單-->
<menuitem name="bug管理系統" id="bug_manage.menu_root"/>
<!--二級選單-->
<menuitem name="bug管理" id="bug_manage.menu_1" parent="bug_manage.menu_root"/>
<!--三級選單 ==> 選單和動作聯絡-->
<menuitem name="bug列表" id="bug_manage.menu_1_list" parent="bug_manage.menu_1"
action="bug_manage.action_window"/>
</data>
</odoo>
上述程式碼說明:
- 注意XML是按順序解釋的,所有動作視窗必須要定義在選單之前,否則會報錯
- 上面是比較經典的定義檢視的方式,這裡使用的都是預設的tree,search,form,如果暫時不能理解,就先記住
我們需要在__manifest__.py
的data
中宣告新增的bugs.xml
檔案(注意: 但凡是新增的xml和csv檔案,都是需要在__manifest__.py
中data
欄位中進行宣告)
'data': [
'security/ir.model.access.csv',
'views/views.xml',
'views/templates.xml',
'views/bugs.xml',
],
然後如果需要看到效果的話,還需要設定一下安全許可權(這裡不理解不要緊,先照做),在編輯security\ir.model.access.csv
,新增如下程式碼
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_bm_bug,bug_manage.bm_bug,model_bm_bug,,1,1,1,1
如果你使用 -dev=all
引數,或者直接重啟伺服器,可以看到下圖介面
因為我們所用的是系統生成的列表檢視,所以顯示的資訊只有一個name
欄位
- 之前我們說過
name
是特殊欄位(注意是name
不是_name
,後者是model的唯一標識),可以用來代表一條記錄 - 但是如果僅僅只有這個欄位,使用起來很不方便,我們需要更多欄位欄位資訊,就需要建立自己的
tree
檢視
建立列表(tree)檢視
檢視資料都是儲存在資料庫ir.ui.view
模型中
- 若要向這個模型中新增之間的檢視,則需要我們在XML檔案中使用
<record>
元素 - 系統會在模組安裝後解析這些XML檔案,將
<record>
元素中的內容插入到模型中
下面我們仍然在views/bugs.xml
檔案中新增該列檢視程式碼
<!--自定義列表(tree)檢視-->
<record model="ir.ui.view" id="bug_manage.list">
<field name="name">bug_manage list</field>
<field name="model">bm.bug</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="is_closed"/>
<field name="user_id"/>
</tree>
</field>
</record>
業務文件表單(form)檢視
我們在此直接跨過了普通表單檢視的操作介紹,其實普通表單檢視與前面介紹的列表檢視非常相似,只是將<tree>
和<list>
互換一下.
業務表單檢視就是在普通表單檢視的基礎上新增了<header>
和<sheet>
兩個元素,我們還是在views/bugs.xml
檔案內增加如下程式碼
<record model="ir.ui.view" id="bug_manage.form">
<field name="name">bug_manage form</field>
<field name="model">bm.bug</field>
<field name="arch" type="xml">
<form>
<header>
<!--這裡的 button的name對應於未來後臺的方法-->
<button name="do_close" type="object" string="關閉bug"/>
</header>
<sheet>
<group name="group_top" col="2">
<field name="name"/>
<field name="user_id"/>
<field name="is_closed"/>
</group>
<group name="group_right">
<field name="close_reason"/>
<field name="follower_ids"/>
</group>
<notebook>
<page string="詳細內容">
<field name="detail"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
搜尋(search)檢視
到目前為止,我們的應用已經包含了列表檢視和表單檢視,而且都做了一些優化,但是現在還存在一個問題: 如果列表的記錄變多了,檢視起來不是很方便,我們還需要搜尋功能還輔助提高效率.
在沒有增加搜尋檢視之前,列表檢視的右上方已經存在一個搜尋框,這是Odoo自動新增的基於name
欄位的搜尋檢視,也就是說只能通過name
(這裡再次體現了name
作為特殊欄位的用途)
下面就來增加一個搜尋檢視,使列表檢視可以基於name
,is_closed
,user_id
進行搜尋,仍然是在views/bugs.xml
中新增程式碼
<!--搜尋檢視-->
<record model="ir.ui.view" id="bug_manage.search">
<field name="name">bug_manage search</field>
<field name="model">bm.bug</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="is_closed"/>
<field name="user_id"/>
</search>
</field>
</record>
搜尋檢視往往會和預定義篩選條件一起使用,這部分內容將會結合後端檢視一起介紹.
檢視繼承
前面我們已經介紹了模型的繼承,同樣,Odoo也支援對檢視進行繼承操作.
- 檢視的繼承也是在XML程式碼中開發的
- 使用
inherit_id
屬性引用被繼承的檢視,然後就可以在子檢視中進行增強擴充套件.
結合我們的應用,關注者就算在res.partner
模型的基礎之上增加bug列表(bug_ids)欄位的,所以關注者的檢視也繼承自res.partner
檢視進行修改.(模型是繼承的,檢視也是繼承的就合情合理了)
現在,第一步就是要確定具體繼承自哪一個檢視(需要知道該檢視的XML ID),進入 設定 | 使用者介面 | 檢視,搜尋res.partner
檢視檢視對應的外部ID
第二步需要找到具體的錨點,以此來確認我們的bug_ids
最終會載入檢視的哪個位置.
- 通常是尋找一個有
name
屬性的元素 - 這裡我們以
<field name="mobile">
元素作為錨點
確定好要繼承檢視的外部ID和找到對應的錨點,我們可以開始進行檢視繼承了. 新建views/follower.xml
檔案並 編輯程式碼
<odoo>
<data>
<!--現在只是在 res.partner的表單中加入一個 bug_ids 的欄位-->
<record model="ir.ui.view" id="bug_manage.follower_form">
<field name="name">follower_form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="mobile" position="after">
<field name="bug_ids"/>
</field>
</field>
</record>
<record model="ir.actions.act_window" id="bug_manage.follower_action_window">
<field name="name">follower_action_window</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="follower管理" id="bug_manage.follwer_menu" parent="bug_manage.bug_menu_root"/>
<menuitem name="follower列表" id="bug_manage.follwer_menu_list" parent="bug_manage.follwer_menu"
action="bug_manage.follower_action_window"/>
</data>
</odoo>
程式碼說明:
<field name="inherit_id" ref="繼承檢視的XML ID" />
這是檢視繼承必須有的- 這些選擇
<field name>
中的name
進行定位,其實還可以通過<XPath>
進行定位,這個在後面會講到
在__manifest__.py
中引入此檔案,更新應用後,可以看到
業務邏輯
在瞭解了模型和檢視以後,下面就需要關注的就是一些邏輯的具體處理.
現在我們來實現關閉bug按鈕的邏輯,邏輯在Python類中可以使用方法來完成,在檔案models/bugs.py
的Bugs類中增加如下程式碼
@api.multi
def do_close(self):
for item in self:
item.is_closed = True
return True
更新應用後,開啟一個bug(沒有可以新建),點選關閉bug按鈕,會發現是否關閉複選框已被選中.
安全性配置
Odoo可以通過安全性配置來設定到選單級和到模型級的許可權,可以具體到操作的檢視,編輯和刪除等明細操作,本節我們就來介紹一下Odoo的安全性配置.
訪問控制
我們一直使用admin使用者進行開發測試,其實,目前普通使用者還不能正常使用bug管理系統,因為我們還沒有對該系統做任何訪問設定(在Odoo12開始,admin也需要做許可權設定才能訪問)
要想檢視訪問控制相關的資訊,需要登入前端並進入 設定 | 安全 | 訪問列表 頁面.
上圖我們可以看到模型與安全組的對應關係,以及其具備的增刪改查的具體許可權.
這些訪問許可權資料全部儲存在ir.model.access
模型中,在我們的專案中,可以通過檔案在系統啟動時寫入該模型.
- 因為我們是使用腳手架建立的專案,所以專案的
security
路徑下已經預設建立了一個檔案:ir.model.acccess.csv
- 注意:
模型名.csv
這是是規定,不能修改,這個CSV檔案會將記錄寫入對應的模型中
修改完成後不要忘記在__manifest__.py
中新增該csv檔案
'data': [
# 注意: 許可權控制檔案要放在開頭
'security/ir.model.access.csv',
'views/views.xml',
'views/templates.xml',
'views/bugs.xml',
'views/follower.xml',
],
這裡還沒有建立許可權組,如果要進行進行許可權演示的話,需要在後面講到建立許可權組在進行演示.
注意: 除了模型了訪問控制,還有行級訪問規則,其具體的記錄是存放在ir.rule
模型中的,關於這點也是後面再說.
網頁和控制器
Odoo還提供了一個Web開發框架,可用於實現與後端功能高度整合的網站功能.
- 我們在本節將會開發簡單的網站網頁用於顯示我們的bug列表,是使用者可以通過URL(
http://<serverhost>:8069/bug_manage
)訪問我們的頁面 - 本節只做最基本的介紹,更深入的內容會在第11章進行詳細介紹
Web控制器是負責網頁轉發的元件,具體在技術上就是http.Controller
類中定義的方法,這些方法綁定了終端頁面的URL.
- 當URL被訪問時,控制器程式碼就會執行相應的操作將HTML呈現給使用者.
- 對於HTML的渲染,Odoo提供了QWeb模板引擎.
應用的控制器程式碼都放在/controllers
資料夾下,在此目錄中使用系統預設建立的檔案controllers.py
,編輯如下程式碼
from odoo import http
class Bug(http.Controller):
@http.route('/bug_manage')
def bug_manage(self, **kw):
bugs = http.request.env['bm.bug'] # 獲取 bm.bug 記錄集
domain_bug = [('is_closed', '=', False)] # 搜尋條件
bugs_open = bugs.search(domain_bug) # 返回符合搜尋條件的記錄集
return http.request.render('bug_manage.bugs_templates', {'bugs_open': bugs_open})
上面最後使用了return http.request.render('bug_manage.bugs_templates',{})
,那麼接下來我們就來建立bugs_template
這個QWeb
模板
<odoo>
<data>
<template id="bugs_templates" name="bug">
<div class="container">
<h1>未關閉的bug</h1>
<t t-foreach="bugs_open" t-as="bug">
<div class="row">
<span t-field="bug.name"/>
</div>
</t>
</div>
</template>
</data>
</odoo>
儲存上述XML程式碼,不要忘記加入__manifest__.py
檔案中,然後更新應用,訪問http://localhost:8069/bug_manage
,可以看到下圖效果
總結
本章我們建立了一個bug管理模組,介紹了應用從建立到升級的全流程,並且按照模型,檢視,業務邏輯對應用進行了介紹.
讀者應該掌握了
- 應用專案的基本架構
- 知道如何 調整Python程式碼和XML檔案以建立自己想要的介面
- 能夠了解Odoo中繼承的應用
本章還介紹了應用的安全性配置,包括訪問控制,網頁及控制器,讀者應該能夠根據需求進行許可權配置