學習筆記
內容均來自http://blog.sunansheng.com/python/odoo/odoo.html
odoo深入淺出來發教程
----Odoo開發基礎: 請假模塊進階
__init__.py 文件沒啥好改動的,然後我們再看到main_model.py文件,這一次進行了較多地方的改動。
from openerp import models, fields, api import logging class Qingjd(models.Model): _name = 'qingjia.qingjd' name = fields.Many2one('hr.employee', string="申請人", readonly=True) manager = fields.Many2one('hr.employee', string="主管",readonly=True) beginning = fields.Datetime(string="開始時間", required=True, default = fields.Datetime.now()) ending = fields.Datetime(string="結束時間", required=True) reason = fields.Text(string="請假事由",required=True) accept_reason = fields.Text(string="同意理由",default="同意。") #########compute 沒有寫入數據庫 on the fly 可以被workflow的condition調用 current_name = fields.Many2one('hr.employee', string="當前登錄人", compute="_get_current_name") is_manager = fields.Boolean(compute='_get_is_manager') ###### state = fields.Selection([ ('draft', "草稿"), ('confirmed', '待審覈'), ('accepted', '批准'),('rejected', '拒絕'), ], string='狀態',default='draft',readonly=True) @api.model#使用新的api def _get_default_name(self): uid = self.env.uid res = self.env['resource.resource'].search([('user_id','=',uid)]) name = res.name employee = self.env['hr.employee'].search( [('name_related','=',name)]) # for i in self.env.user:# 說明其是recordset # print('hello') return employee #------------ @api.model def _get_default_manager(self):#單記錄recordset可以直接用點記號讀取屬性值 uid = self.env.uid res = self.env['resource.resource'].search([('user_id','=',uid)]) name = res.name employee = self.env['hr.employee'].search( [('name_related','=',name)]) logging.info("myinfo {}".format(employee.parent_id)) return employee.parent_id # 似乎有這種數字引用方法值得我們注意 _defaults = { 'name' : _get_default_name , 'manager' : _get_default_manager , } #----- def _get_is_manager(self):###這裏return不起作用 print('----------test') print(self.current_name, self.manager,self.env.uid) if self.current_name == self.manager: self.is_manager = True else: self.is_manager = False #----- def _get_current_name(self): uid = self.env.uid res = self.env['resource.resource'].search([('user_id','=',uid)]) name = res.name employee = self.env['hr.employee'].search( [('name_related','=',name)]) self.current_name = employee ############################## #----- def draft(self, cr, uid, ids, context=None): if context is None: context={} self.write(cr,uid,ids,{'state':'draft'},context=context) return True #----- def confirm(self, cr, uid, ids, context=None): if context is None: context={} self.write(cr,uid,ids,{'state':'confirmed'},context=context) return True #----- def accept(self, cr, uid, ids, context=None): if context is None: context={} self.write(cr,uid,ids,{'state':'accepted'},context=context) print('你的請假單被批准了') return True #----- def reject(self, cr, uid, ids, context=None): if context is None: context={} self.write(cr,uid,ids,{'state':'rejected'},context=context) print('抱歉,你的請假單沒有被批准。') return True
讀者不要着急,這裏的內容我會慢慢講解的。然後views.xml改成了這個樣子:
<?xml version="1.0"?> <openerp> <data> <!-- 打開請假單動作--> <act_window id="action_qingjia_qingjd" name="請假單" res_model="qingjia.qingjd" view_mode="tree,form" /> <!--表單視圖--> <record id="qingjia_qingjd_form" model="ir.ui.view"> <field name="name">qing jia dan form</field> <field name="model">qingjia.qingjd</field> <field name="arch" type="xml"> <form> <header> <button name="btn_confirm" type="workflow" states="draft"string="發送" class="oe_highlight" /> <button name="btn_accept" type="workflow" states="confirmed"string="批准" class="oe_highlight"/> <button name="btn_reject" type="workflow" states="confirmed"string="拒絕" class="oe_highlight"/> <field name="state" widget="statusbar" statusbar_visible="draft,confirmed, accepted,rejected" class="oe_highlight" type="workflow"/> </header> <sheet> <group name="group_top" string="請假單"> <group name="group_left"> <field name="name"/> <field name="beginning"/> </group> <group name="group_right"> <field name="manager"/> <field name="ending"/> </group> </group> <group name="group_below"> <field name="reason"/> </group> </sheet> </form> </field> </record> <!--tree視圖--> <record id="qingjia_qingjd_tree" model="ir.ui.view"> <field name="name">qing jia dan tree</field> <field name="model">qingjia.qingjd</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="beginning"/> <field name="ending"/> <field name="state"/> </tree> </field> </record> <!--加入菜單--> <menuitem id="menu_qingjia" name="請假" sequence="0"></menuitem> <menuitem id="menu_qingjia_qingjiadan" name="請假單" parent="menu_qingjia"></menuitem> <menuitem id="menu_qingjia_qingjiadan_qingjiadan" parent="menu_qingjia_qingjiadan" action="action_qingjia_qingjd"></menuitem> </data> </openerp>
除了跟着main_model.py文件裏面的一些修改而更改外,最值得一提的就是button的 type 屬性設置爲了workflow
如果還是設置爲 "object" ,當你點擊按鈕的時候,該模型就應該提供對應按鈕name 的一個方法,這個方法將執行某
些動作。這裏要強調的就是這樣簡單的object按鈕是不和後面談到的工作流的概念兼兼容的。如果按鈕的type還是設
置爲object,那麼其是不發送工作流的signal的。更多工作流的細節等下再談。
然後還編寫了一個工作流的workflow.xml文件:
<?xml version="1.0" ?> <openerp> <data noupdate="0"> <record id="wkf_qingjia" model="workflow" > <field name="name">wkf.qingjia</field> <field name="osv">qingjia.qingjd</field> <field name="on_create">True</field> </record> <!----------> <record id="act_draft" model="workflow.activity" > <field name="wkf_id" ref="wkf_qingjia" /> <field name="name">draft</field> <field name="flow_start" eval="True"/> <field name="kind">function</field> <field name="action">draft()</field> </record> <!----------> <record id="act_confirm" model="workflow.activity" > <field name="wkf_id" ref="wkf_qingjia" /> <field name="name">confirm</field> <field name="kind">function</field> <field name="action">confirm()</field> </record> <!----------> <record id="act_accept" model="workflow.activity" > <field name="wkf_id" ref="wkf_qingjia" /> <field name="name">accept</field> <field name="kind">function</field> <field name="flow_stop">True</field> <field name="action">accept()</field> </record> <!----------> <record id="act_reject" model="workflow.activity" > <field name="wkf_id" ref="wkf_qingjia" /> <field name="name">reject</field> <field name="kind">function</field> <field name="action">reject()</field> </record> <!----------> <record model="workflow.transition" id="qingjia_draft2confirm"> <field name="act_from" ref="act_draft" /> <field name="act_to" ref="act_confirm" /> <field name="signal">btn_confirm</field> </record> <!----------> <record model="workflow.transition" id="qingjia_confirm2accept"> <field name="act_from" ref="act_confirm" /> <field name="act_to" ref="act_accept" /> <field name="signal">btn_accept</field> <field name="condition">is_manager</field> </record> <!----------> <record model="workflow.transition" id="qingjia_confirm2reject"> <field name="act_from" ref="act_confirm" /> <field name="act_to" ref="act_reject" /> <field name="signal">btn_reject</field> <field name="condition">is_manager</field> </record> </data> </openerp>
然後 __openerp__.py
文件沒做什麼修改,就把上面的workflow.xml加進來,然後depends將base改爲了hr,因爲等下一些功能是依賴官方內置模塊hr(人資管理模塊)的。
如圖
這麼一張簡單的圖並沒很好地第二版的加強功能說明出來,建議讀者新建幾個用戶,然後在人力資源那
裏把員工和部門設置好,尤其是部門主管等信息。然後讀者可以嘗試以一個普通員工來新建一個請單,
編輯,保存,發送。然後接下來的批准和拒絕按鈕該普通員工是不能點擊的(工作流的condition控制
的),而只有主管纔可以正常點擊批准或拒絕按鈕。
----10.1 本例涉及到的數據庫表格簡介
這裏我只進行簡略的討論,具體該表格的細節請讀者自己用pgadmin3軟件查看之。
res.uses:在網頁視圖下對應的菜單是: 設置→用戶→用戶.這個表格(或說模型)存儲着一些登錄
用戶的信息,比如用戶名或密碼等。
res.groups:在網頁視圖下對應的菜單是: 設置→用戶→組.權限管理就是利用了這個表格建立的羣
組概念,比如最常使用的羣組,人力資源/僱員就是這個表格的第五條記錄,其外部是base.group_user
resource.resource:res.uses存儲的只是用戶的登錄名和密碼等,而在網頁上具體顯示的是該用戶更
人類易讀的名字,其就是根據這個表格來完成的。這個表格有user_id,該id就對應res.uses的某條記
錄,而name就是對應的人類易讀的名字.
hr.employee:在網頁視圖下對應的菜單是: 人力資源→人力資源→員工。這個表格存儲着員工的一些
信息,其中name_related屬性就對應前面resource.resource的name,然後parent_id對應該員工的上
一級也就是主管,具體就是本表格的對應id記錄,然後department是該員工所屬部門,具體是
hr.department表格中的對應id的記錄.
hr.department:在網頁視圖下對應的菜單是: 人力資源→設置→部門.本表格記錄了公司的部門信息
(支持多公司概念,有company_id標識),還有該部門的主管對應的員工id.
----工作流概念入門
工作流對象算是Odoo框架裏面頗具特色的一部分,其在網頁視圖中對應的菜單是: 設置→工作流→工作流。對應的模型是 workflow ,但並沒有workflow這個表格,要說對應的話應該是wkf這個表格,然後還有wkf_activity表格等。
Odoo工作流的圖標視圖如下圖所示是一個不錯的查看和管理當前工作流程的工具:
其中節點叫做“活動(activity)”,然後弧線連接叫做“轉變(transition)”。活動描述了Odoo服務器應該完成的一些工作,比如:改變某些記錄的狀態,發送email等等。轉變則控制了從活動到活動之間的工作流。
轉變可以增加屬性如條件、信號、觸發器等。這樣工作流的行爲是取決於用戶的動作(如點擊某個按鈕),或某個記錄值的更改或任意的python代碼。總而言之,Odoo工作流系統提供了:
關於記錄如何演變的描述
根據多樣的彈性的條件建立自動化行動機制
管理公司流程和確認規則
管理對象間的互動
在他們的生命期內一個可視化的流程圖
工作流和模型關聯在一起,具體某個模型新建一個對象就會是實例化一個工作流對象。然後該工作流對象應該有個狀態記錄,服務器重啓之後工作流還是在那個狀態下,也就是已經執行了的都不會執行了
--------定義工作流對象
<record id="wkf_qingjia" model="workflow" > <field name="name">wkf.qingjia</field> <field name="osv">qingjia.qingjd</field> <field name="on_create">True</field> </record>
首先是定義了本工作流對象,model=workflow,id是多少,這個id很重要,等下本工作流對象裏面的活動和轉變都要用到這個id。
然後field定義了
name:本工作流的name,隨意。
osv:這個屬性很重要,是具體的那個模型,其和本工作流關聯起來了。
(no term):on_create 一般設置爲True,工作流會根據每一個模型新建一個對象再實例化一次。----創建節點
<record id="act_draft" model="workflow.activity" > <field name="wkf_id" ref="wkf_qingjia" /> <field name="name">draft</field> <field name="flow_start" eval="True"/> <field name="kind">function</field> <field name="action">draft()</field> </record>
這裏創建了一個節點活動對象:
flow_start:如果設置爲True 工作流會從這裏開始
wkt_id:屬於某個工作流對象 我們看到這裏的ref語法如果是引用本模型內的對象則可以省略。
kind:有四種 dummy function subflow stop all
action:具體的動作(於模型上)
flow_stop:一個工作流的完成就是所有的活動有flow_stop屬性的都設置爲了 True
--------創建鏈接
<record model="workflow.transition" id="holiday_draft2confirm"> <!-- 1. draft->submitted (confirm signal) --> <field name="act_from" ref="act_draft" /> <field name="act_to" ref="act_confirm" /> <field name="signal">confirm</field> <field name="condition">can_reset</field> <field name="group_id" ref="base.group_user"/> </record>
轉變對象屬於 workflow.transition 模型。
act_from 何活動出發 act_to 到何活動去
signal:信號,觸發本遷移的信號,如果定義了信號,則只有在系統收到signal定義的信號後,纔會觸
發本遷移。即使本次遷移滿足條件,但如果沒有收到信號,也不會觸發。
condition:遷移的條件,是一段Python代碼,通常是一個函數調用。當系統收到signal中定義的信號時
候,會在收到信號後檢查此處的條件,條件爲真則觸發遷移。如果沒有定義信號,但定義了條件時,
在滿足條件的情況下才會觸發遷移。如果不滿足條件,系統會在相關數據發生變化後再次檢查該條件
是否滿足。
--Odoo模型層詳解
經過前面得介紹,我們也確實感覺到Odoo得ORM層得API應該是Odoo技術框架得最核心部分,如果我們翻
翻Odoo框架源碼,也會看到models.py那個文件有六七千行代碼,這也說明Odoo得設計者在編寫ORM這塊
花費了很多精力,所以我想我們把Odoo得ORM層API這部分知識掌握了,Odoo框架得神祕面紗也基本上被掀開一大半了
本章在前面得基礎上繼續詳細介紹Odoo ORM API細節.
----_name
定義了本模型具體對應SQL表格得名字,比如前面定義得mymdule.fruits對應數據庫中得表格名是
public.mymodule_fruits
----各個表頭屬性
class Fruits(models.Model): _name = 'mymodule.fruits' name = fields.Char()
如上所示這樣定義一個name屬性就對應SQL中得一個表頭名,即名叫name得一列.這個我們在前面看到了
然後上面得fields.char()具體定義了一個字符串輸入字段,類似得還有fields.Boolean布爾值;
fields.Integer()整數值;fields.Float浮點數值;fields.Text和char類似,但通常用於多行文本輸入;
fields.Selection幾個值得選擇;fields.Html;fields.Date;fields.Datetime等.這些都是所謂得簡單
字段輸入,此外還有一種關係字段,其是用於描述表格之間得關係得(相同或者不同模型)
fields.Char()函數可以接受一些可選參數,比如string表示本模型爲用戶可見得名字;required接收
一個布爾值,默認False,若是True,則該字段不可以爲空。help在用戶UI界面下得幫助信息;index布爾值
默認False,若爲True則要在數據庫中爲這列創建一個索引(index)
此外還有一些其他表頭字段:
ID:在表格中一條記錄獨特id
create_uid:誰創建的
create_date:創建日期
write_date:最後修改日期
write_uid:誰修改得
----name字段
Odoo中得模型一般都還需要name字段,用於各種搜索或者顯示行爲.
----具體模型得數據
具體模型數據用xml文件來聲明
<openerp> <data> <record id="apple" model="mymodule.fruits"> <field name="name">apple</field> </record> </data> </openerp>
這裏得record元素你可以理解爲SQL表格得一條記錄,或者Odoo模型具體得一個實例對象,然後id屬性
特別記錄了這條數據(說可被外調用,具體還不清楚);model屬性就是這個對象具體對應那個模型
然後裏面得field元素你可以看作記錄具體某個name表頭得字段,field body裏面就放着這個字段得值
這裏得record在視圖中對應得應該是basic view;此外還有tree對應得是列表視圖;form對應表單視圖
----模型間得關係
many2one
one2many
many2many
----工作流
一個工作流模型 在Session模型上都加入了state 字段 : 有三種字段, Draft Confirmed Done
有效的轉變有:
Draft → Confirmed Confirmed → Draft Confirmed → Done Done → Draft