1. 程式人生 > >「Odoo 基礎教程系列」第六篇——從 Todo 應用開始(5)

「Odoo 基礎教程系列」第六篇——從 Todo 應用開始(5)

Powered By @adigold1

大家好鴨,我又來更新啦!還記得我們在第二篇教程中提到過的動作(actions)嗎,今天我們就來專門講講在 Odoo 中的 action,學習不同型別的動作對應的應用場景,並且在我們的 Todo 應用中使用上其中一些型別的動作。

視窗動作

視窗動作在 Odoo 中是最常見也是最常用的動作型別,在第二篇教程中我們建立選單的時候就已經用過視窗動作了,我們開啟 menus.xml 來看看一個視窗動作的組成:

    <!-- menus.xml -->
    <record id="action_todo_task" model="ir.actions.act_window">
        <field name="name">待辦事項</field>
        <field name="res_model">todo.task</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form</field>
        <field name="target">current</field>
    </record>

這裡的 model 之前我們也說過,對應的是這個動作的型別(每個型別的動作都對應一個模型),我們重點來看看下面列出的欄位以及沒有列出來的欄位,分別說明一下它們各自的作用。

  • res_model:要開啟的檢視(視窗)關聯的資料模型
  • view_type:檢視型別,預設值為 form,一般情況下我們取預設值就可以了
  • view_mode:允許開啟的檢視型別,以逗號分隔,預設值為 tree,form
  • target:開啟的視窗型別,常用的有 current(當前視窗開啟)和 new (彈窗開啟)這兩種,預設為 current

除了上述列出的一些欄位外,還有一些非必填的欄位在某些時候我們是會用上的,這裡也分列出來:

  • view_ids:關聯的檢視物件 id,需注意區分和 view_id 的區別
  • view_id:關聯的檢視的 id, 例如在不同時候需要開啟同一個資料模型不同的表單檢視,就可以通過這個欄位指定要開啟的檢視的 id
  • res_id:僅在檢視型別為 form 時有效,表示開啟該 id 對應的記錄的表單檢視,如未指定則開啟新建頁面
  • context:傳遞到上下文中的資料,一個字典
  • domain:過濾規則,對檢視中的記錄進行過濾
  • limit:列表中每頁顯示的記錄數量,預設為 80
  • search_view_id:指定搜尋檢視,不指定則按預設規則載入
  • multi:如果設定為 True 且動作綁定了模型(src_model
    )的話,該動作按鈕會只出現在所繫結模型列表檢視的「動作」下拉列表中(在搜尋檢視左側)
  • views:由 (view_id, view_type) 這樣的元組對組成的列表,view_id 為指定檢視的 id 或是 False(按預設值取出對應檢視),view_type 表示檢視型別

其中 views 是一個計算欄位,該欄位會根據 view_ids, view_modeview_id 自動計算出來,大致分為三個步驟:

  1. view_ids 中順序取出 id 和檢視型別,組成 (view_id, view_type) 元組對加入到 views 列表中
  2. 若定義了 view_id 且對應的檢視型別不在 view_ids 中,則加入到 views
  3. 最後對 view_mode 中指定的檢視型別,但是不存在於 view_idsview_id 中,則將其 view_id 置為 False 組成元組對後加入到 views

這裡需要強調一點的是,如果我們在某些時候需要直接通過 Python 程式碼或 JS 程式碼開啟一個視窗動作,此時我們將需要自己新增 views 欄位,否則將會丟擲錯誤,具體的例項我們將在之後使用到了再進行講解分析。

伺服器動作

Server Action 可以在服務端執行復雜的邏輯程式碼,是非常強大的一種動作型別。我們先給代辦事項新增一個 Server Action 用於快速完成任務的動作,這樣我們就不用每次都要點進去一個任務然後進入編輯狀態再勾選儲存這麼多步驟了。

我們來看看定義一個最簡單的 Server Action 應該包含哪些內容:

    <!-- views.xml -->
    <record id="action_mark_todo_task_done" model="ir.actions.server">
        <field name="name">標記完成</field>
        <field name="model_id" ref="model_todo_task"/>
        <field name="binding_model_id" ref="model_todo_task"/>
        <field name="state">code</field>
        <field name="code">records.write({'is_done': True})</field>
    </record>

和其他所有在資料檔案(XML)中定義的資料一樣,首先是一個包含屬性 idmodel<record /> 標籤,我們要定義的是 Server Action, 所以需要將 model 設定為 ir.actions.server,然後是對應 Server Action 模型下的一些欄位:

  • model_id:當前的動作是在哪個模型上執行的
  • binding_model_id:繫結的模型,當前動作將會出現在繫結的模型的檢視中
  • state:伺服器動作的型別,總共有 4 種可選的型別,分別是 code(執行 Python 程式碼),object_create(建立一條新記錄),object_write(更新記錄),multi(執行多個動作)
  • code:對應 state 的型別 code,為當前動作執行時所要執行的 Python 程式碼

大家應該都留意到了在上面的定義中出現了一個屬性 ref,如果我沒有理解錯,它應該是 reference 的縮寫,它的值是一個外部 ID(external id),我們上面定義的這個動作的外部 ID 就是定義時新增的 id 屬性的值 action_mark_todo_task_done,它指向一條具體的記錄(就像 Many2one 欄位一樣)。

我們定義的所有模型都會在 ir.model.data 對應的表中存在相應的記錄,這些模型的外部 ID 形如 model_model_name,其中 model_name 是將模型名 [model.name](http://model.name). 替換成 _,例如我們的 todo.task 模型的外部 ID 就是字首 model_ 加上 todo_task 組成的 model_todo_task 了。

接下來我們再看到欄位 code 裡面的內容,在這裡面我們有一些變數是可以直接使用的:

  • env:Odoo 的執行環境
  • model:動作觸發時對應的 Odoo 模型例項
  • record:動作觸發時對應的單個記錄(如在表單檢視中執行對應當前表單所指向的記錄),可能是空的
  • records:動作觸發時對應的記錄集(如在列表檢視中勾選多條記錄觸發,記錄集指向這些選中的記錄),可能是空的
  • Python 庫:time, datetime, dateutil, timezone 時間相關的 Python 庫
  • log:用來記錄日誌資訊
  • Warning:通過 raise Warning('xxxxx') 丟擲警告資訊

除了上述可以直接使用的變數外,還有一個 action 變數,當我們想讓當前動作執行完畢之後,返回一個新的動作繼續執行,就可以將返回的動作的定義(一個字典)賦值給變數 action,客戶端將會自動執行該動作。

OK, 前面說了這麼多,我們更新一下程式碼,然後重新整理瀏覽器看看效果吧:

Server Action

在列表檢視中,當我們勾選了任意的記錄後,將會出現一個「動作」選單,開啟之後就可以看到我們剛剛定義的「標記完成」這個 Server Action 了,試試看執行會發生什麼,注意觀察「已完成?」這一列的變化。然後再建立一條新的記錄,在表單頁面中,同樣有「動作」選單,我們在這裡也可以執行「標記完成」的動作,怎麼樣,是不是比原本的編輯再勾選再儲存要方便得多了,而且我們還能在列表裡勾選多條記錄進行批量操作,不能更方便了!

受限於篇幅,我們這裡只涉及了 statecode 型別的伺服器動作,相對其他三種類型,已經足以應付絕大多數場景的需求了,這裡就不展開細說,感興趣的同學可以看看官方模組的相關實現,要學會看原始碼哈!

URL 動作

URL Action 用來開啟一個網頁連結,十分簡單的一種動作型別:

    <record id="action_open_google" model="ir.actions.act_url">
        <field name="name">開啟谷歌</field>
        <field name="target">new</field>
        <field name="url">https://google.com</field>
    </record>

上面這個動作觸發後將會新開瀏覽器視窗(或新標籤)開啟谷歌首頁,主要的兩個欄位如下:

  • target:有兩個可選值,分別是新視窗(new)開啟連結,相當於 <a target='_blank' />,以及當前視窗(self)開啟,相當於 <a target='_self' />
  • url:要開啟的目標頁面的連結,可以是外部頁面也可以是同域下的內部頁面

客戶端動作

觸發一個完全由客戶端(瀏覽器)執行的動作,例如某些模組的「儀表板」就屬於 Client Action,它的基本組成如下:

    <record id="backend_dashboard" model="ir.actions.client">
        <field name="name">Dashboard</field>
        <field name="tag">backend_dashboard</field>
    </record>

上面這個 Client Action 是在 Odoo 自帶模組 website 中定義,用來開啟儀表板,來看看客戶端動作有哪些欄位是可用的:

  • tag:客戶端動作的標識,需要是在 action_registry 中註冊了的動作
  • target:同窗口動作
  • context:同窗口動作
  • params:傳遞給客戶端動作的引數,一個字典

客戶端動作的本體實際上是 tag 所指向的動作,這個動作是用 JS 編寫的一些邏輯(在 Odoo 的 JS 框架下),在之後會有專門的教程教大家相關的內容,這裡請先略過。

報表動作

這型別的動作用於觸發報表列印,例如打印發票等。這裡不對該型別作介紹,感興趣的同學同樣可以去看看 Odoo 自帶的一些模組如 account 等。

瞎說幾句

在實際開發中,最常接觸和使用的動作基本上就視窗動作(ir.actions.act_window)和伺服器動作(ir.actions.server)這兩種了,其中 Server Action 最複雜也最強大,可以用它來實現很多的功能。

上面沒有展開細說的內容,希望大家可以多多翻看官方模組的實現,學會閱讀原始碼才是最好的學習方式。

教程中的程式碼會更新在 GitHub 倉庫「Odoo-Tutorial-Demo」中,如果遇到什麼問題,歡迎提出,我會及時解答 ;-)