odoo 開發入門教學系列-一些使用者介面

2023-03-25 21:00:53

一些使用者介面

資料檔案 (XML)

參考: 該主題關聯檔案可以檢視Data Files.

上一章,我們通過CSV檔案新增了資料。當需要新增資料格式簡單時,用CSV格式還是很方便的,當資料格式更復雜時(比如檢視架構或者一個郵件模板),我們使用XML格式。比如包含HTML tags的 help field。雖然可以通過CSV檔案載入這樣的資料,但是使用XML更方便。

類似CSV檔案,XML檔案也必須按約定新增到合適的目錄,並在 __manifest__.py中進行定義。資料檔案中的內容也是在模組安裝或者更新時按序載入。因此,對CSV檔案所做的所有說明對XML檔案都適用。當資料連結到檢視時,我們將它們新增到views資料夾中

本章,我們將通過XML檔案載入我們第一個action和選單。Actions 和選單為資料庫中的標準記錄。

註解:

當程式很注重效能時,CSV格式優先於XML格式。這是因為,在odoo中載入CSV檔案比載入XML檔案更快。

odoo中,使用者介面(action,選單和檢視)大部分是通過建立和組裝XML檔案中的記錄來定義的。常見的模式為 選單> action > 檢視。為了存取記錄,使用者在幾個選單級中導航。最深層是觸發開啟記錄列表的action。

操作(Actions)

參考: 主題相關檔案可以檢視 Actions.

動作可以通過三種方式觸發 :

  1. 點選選單專案(連結接到指定動作)
  2. 點選檢視按鈕(如果與action關聯)
  3. 物件的上下文action

本章僅涵蓋第一種情況。 我們Real Estate例子中,希望將一個選單連線到 estate.property model, 以便建立一個新記錄。 action可以視為選單和model之間的連結

test.model 的基本action:

<record id="test_model_action" model="ir.actions.act_window">
    <field name="name">Test action</field>
    <field name="res_model">test.model</field>
    <field name="view_mode">tree,form</field>
</record>
  • id 外部標識。它可以用於參照記錄(不需要知道其在資料庫中的識別符號)。
  • model ir.actions.act_window (Window Actions (ir.actions.act_window))的一個固定值
  • name action名稱
  • res_model action應用的範圍。
  • view_mode 可獲取的檢視。本例中為列表(樹)和表格檢視。

odoo中到處都可以找到例子,但是這個 簡單action的好例子。關注XML 資料檔案結構,因為你在後續的練習中會用到。

<?xml version="1.0"?>
<odoo>
    <record id="crm_lost_reason_view_search" model="ir.ui.view">
        <field name="name">crm.lost.reason.view.search</field>
        <field name="model">crm.lost.reason</field>
        <field name="arch" type="xml">
            <search string="Search Opportunities">
                <field name="name"/>
                <filter string="Include archived" name="archived" domain="['|', ('active', '=', True), ('active', '=', False)]"/>
                <separator/>
                <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
            </search>
        </field>
    </record>

    <record id="crm_lost_reason_view_form" model="ir.ui.view">
        <field name="name">crm.lost.reason.form</field>
        <field name="model">crm.lost.reason</field>
        <field name="arch" type="xml">
            <form string="Lost Reason">
                <sheet>
                    <div class="oe_button_box" name="button_box">
                        <button name="action_lost_leads" type="object"
                            class="oe_stat_button" icon="fa-star">
                            <div class="o_stat_info">
                                <field name="leads_count" class="o_stat_value"/>
                                <span class="o_stat_text"> Leads</span>
                            </div>
                        </button>
                    </div>
                    <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
                    <div class="oe_title">
                        <div class="oe_edit_only">
                            <label for="name"/>
                        </div>
                        <h1 class="mb32">
                            <field name="name" class="mb16"/>
                        </h1>
                        <field name="active" invisible="1"/>
                    </div>
                </sheet>
            </form>
        </field>
    </record>

    <record id="crm_lost_reason_view_tree" model="ir.ui.view">
        <field name="name">crm.lost.reason.tree</field>
        <field name="model">crm.lost.reason</field>
        <field name="arch" type="xml">
            <tree string="Channel" editable="bottom">
                <field name="name"/>
            </tree>
        </field>
    </record>

    <!-- Configuration/Lead & Opportunities/Lost Reasons Menu -->
    <record id="crm_lost_reason_action" model="ir.actions.act_window">
        <field name="name">Lost Reasons</field>
        <field name="res_model">crm.lost.reason</field>
        <field name="view_mode">tree,form</field>
        <field name="help" type="html">
          <p class="o_view_nocontent_smiling_face">
            Define a new lost reason
          </p><p>
            Use lost reasons to explain why an opportunity is lost.
          </p><p>
            Some examples of lost reasons: "We don't have people/skill", "Price too high"
          </p>
        </field>
    </record>

    <record id="menu_crm_lost_reason" model="ir.ui.menu">
        <field name="action" ref="crm.crm_lost_reason_action"/>
    </record>
</odoo>

練習

estate.property model 建立action。

在適當的位置(本例中為odoo14/custom/estate/models/views)建立 estate_property_views.xml

<?xml version="1.0"?>
<odoo>
    <record id="link_estate_property_action" model="ir.actions.act_window">
        <field name="name">Properties</field>
        <field name="res_model">estate.property</field>
        <field name="view_mode">tree,form</field>
    </record>
</odoo>

修改odoo14/custom/estate/__manifest__.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
    'name': 'estate',
    'depends': ['base'],
    'data':['security/ir.model.access.csv',
            'views/estate_property_views.xml'
            ]
}

重啟服務並觀察檔案載入紀錄檔。

選單(Menus)

參考: 和本主題關聯檔案可以檢視Shortcuts.

為了減少選單(ir.ui.menu)定義和連結到對應action的複雜性,我們可以使用 shortcut

test_model_action 一個的基礎選單:

<menuitem id="test_model_menu_action" action="test_model_action"/>

test_model_menu_action 選單被連結到 test_model_actionaction連結到model test.model。正如前面所述, action可以看做是選單和model之間的連線。

注意:這裡的id的值和action的值不能設定成一樣,否則會報錯。

然而,選單總是遵循一種體系結構,實際上有三個層次的選單:

  1. 根選單,顯示在App切換器中(Odoo社群版切換器是一個下拉式選單)
  2. 第一級選單,顯示在頂部欄中
  3. 動作選單

最容易的方式是在XML檔案中定義結構來建立選單。

test_model_action 定義的一個基礎選單結構:

<menuitem id="test_menu_root" name="Test">
    <menuitem id="test_first_level_menu" name="First Level">
        <menuitem id="test_model_menu_action" action="test_model_action"/>
    </menuitem>
</menuitem>

第三級選單的名稱,直接從action獲取,即為action屬性值

練習

新增選單

在合適的目錄(本例中為odoo14/custom/estate/models/views)建立 estate_menus.xml 檔案

<?xml version="1.0"?>
<odoo>
    <menuitem id="test_menu_root" name="Test">
        <menuitem id="test_first_level_menu" name="First Level">
            <menuitem id="estate_property_menu_action" action="link_estate_property_action"/>
        </menuitem>
    </menuitem>
</odoo>

修改odoo14/custom/estate/__manifest__.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
    'name': 'estate',
    'depends': ['base'],
    'data':['security/ir.model.access.csv',
            'views/estate_property_views.xml',
            'views/estate_menus.xml'
            ]
}

重啟odoo服務,檢視效果

欄位,屬性和檢視(Fields, Attributes And View)

到目前為止,我們只對房產廣告使用了通用檢視,但在大多數情況下,我們希望對檢視進行微調。Odoo有許多微調方式,但通常第一步是確保:

  • 某些欄位有預設值
  • 某些欄位唯讀
  • 當記錄重複時,某些欄位不能被拷貝

在我們的房產業務案例中,我們希望::

  • 售價唯讀(往後將自動填充)
  • 當記錄重複時,可用日期和售價不能被拷貝
  • 臥室數量應該預設為2
  • 預設可用日期應該為3個月

一些新屬性

在進一步進行檢視設計之前,讓我們回到模型定義。我們看到一些屬性,如required=True,會影響資料庫中的表模式。其他屬性也將影響檢視或提供預設值。

練習 -- 新增一些屬性到欄位。

查詢一些合適的屬性 (檢視欄位) 來:

  • 設定售價為唯讀
  • 阻止複製可用日期和售價

修改 odoo14\custom\estate\models\estate_property.pyEstateProperty類屬性expected_priceselling_price的值如下:

expected_price = fields.Float('expected price', digits=(8, 2),  required=True) 
selling_price = fields.Float('selling price', digits=(8, 2), readonly=True, copy=False)

重啟服務和並重新整理瀏覽器介面,我們可以看到無法設定任何售價。複製記錄時,可用日期應為空。

預期效果可參考該動畫連線:https://www.odoo.com/documentation/14.0/zh_CN/_images/attribute_and_default.gif

預設值

可以為任何欄位設定預設值。欄位定義中,新增 default=X, 其中的X 可以是Python文字值(boolean, integer, float, string) ,也可以是一個以model物件自身為入參並返回一個值的函數:

name = fields.Char(default="Unknown")
last_seen = fields.Datetime("Last Seen", default=lambda self: fields.Datetime.now())

例中name欄位預設值為‘Unknown’,而last_seen 欄位預設值為當前時間

練習 -- 設定預設值

新增適當的預設值:

  • 臥室數量預設值為 2
  • 可用日期預設為3個月內

修改 odoo14\custom\estate\models\estate_property.pyEstateProperty類屬性bedroomsselling_price的值如下:

bedrooms = fields.Integer(default=2)
date_availability = fields.Datetime('Availability Date', copy=False, default= lambda self: fields.Datetime.today())

重啟服務和並重新整理瀏覽器介面驗證

保留欄位

參考: 主題相關檔案可參考 保留欄位名稱.

odoo為預定義行為保留了一些欄位名稱。當需要相關行為時,需要在模型中定義這些保留欄位。

練習 -- 新增active欄位

新增一個 active 欄位到estate.property 模型。

修改 odoo14\custom\estate\models\estate_property.pyEstateProperty類,增加active屬性

active = fields.Boolean('Active')

重啟服務,重新整理瀏覽器介面,新增一條記錄,新增時勾選Active核取方塊,即active=True,驗證效果。

預期效果可參考該動畫連結:https://www.odoo.com/documentation/14.0/zh_CN/_images/inactive.gif

注意,已存在的記錄的active欄位預設值為False

練習--為active欄位新增設定

active欄位設定預設值

active 欄位設定適當的屬性值,讓它不再出現在頁面。

練習 -- 新增state欄位

estate.property model新增state 欄位(欄位名可自定義),一個選擇列表。可選值: New, Offer Received, Offer Accepted, SoldCanceled。必選欄位,且不能被拷貝,預設值New

修改 odoo14\custom\estate\models\estate_property.pyEstateProperty類,修改active欄位,增加state欄位

    active = fields.Boolean('Active', default=True, invisible=True) # 注意:實踐發現,invisible欄位不起作用
    state = fields.Selection(
        string='State',
        selection=[('New','New'),
                   ('Offer Received','Offer Received'),
                   ('Offer Accepted', 'Offer Accepted'),
                   ('Sold','Sold'),
                   ('Canceled', 'Canceled')],
        copy=False
    )

重啟服務,驗證效果