odoo12官方文檔翻譯--Building a Module


    <div id="post_detail">

odoo模塊的創建 openacademy學習筆記

odoo -u academy -d academy

openacademy官網完整教程

  • 練習創建模塊
    使用上面的命令行創建一個空模塊Open Academy,並將其安裝在Odoo中。
odoo scaffold openacademy myaddons
  • 練習定義模型,
    在openacademy模塊中定義新的數據模型課程,每門課程包含兩個字段,標題和描述,其中標題是必填字段。編輯文件openacademy/models/models.py以包含Course類。

openacademy/models/models.py

from odoo import models, fields, api

class Course(models.Model):
#定義了兩個字段name,description
_name = 'openacademy.course'

name = fields.Char(string=<span class="hljs-string">"Title"</span>, required=<span class="hljs-keyword">True</span>)
description = fields.Text()

  • 練習定義演示數據,
    添加演示數據以填充Course模型的數據,編輯文件openacademy/demo/demo.xml來添加演示數據

openacademy/demo/demo.xml

<odoo>
    <data>
        <record model="openacademy.course" id="course0">
            <field name="name">Course 0</field>
            <field name="description">Course 0's description

Can have multiple lines
</field>
</record>
<record model="openacademy.course" id="course1">
<field name="name">Course 1</field>
<!-- no description for this one -->
</record>
<record model="openacademy.course" id="course2">
<field name="name">Course 2</field>
<field name="description">Course 2's description</field>
</record>
</data>
</odoo>

demo.xml如何起作用?

必須添加到data中

    'data': [
        # 'security/ir.model.access.csv',
        'views/views.xml',
        'views/templates.xml',
        'demo/demo.xml',
    ],

添加到demo中不起作用

  • 練習定義新菜單項,
    在開放學院菜單項下定義新菜單項來訪問課程。用戶應該能夠:

顯示所有課程的列表
建立或編輯課程
1.建立openacademy/views/openacademy.xml以創建操作和能夠觸發操作的菜單項。
2.添加這個文件到openacademy/__manifest__.py下的data列表。

編寫菜單(menuitem)和行為(action)

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <data>
        <!-- window action -->
        <!-- The following tag is an action definition for a "window action", that is an action opening a view or a set of views -->
        <record model="ir.actions.act_window" id="course_list_action">
            <field name="name">Courses</field>
            <field name="res_model">openacademy.course</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create">Create the first course
                </p>
            </field>
        </record>
    <span class="hljs-comment">&lt;!-- top level menu: no parent --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"main_openacademy_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Open Academy"</span>/&gt;</span>
    <span class="hljs-comment">&lt;!-- A first level in the left side menu is needed
         before using action= attribute --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"openacademy_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Open Academy"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"main_openacademy_menu"</span>/&gt;</span>
    <span class="hljs-comment">&lt;!-- the following menuitem should appear *after*
         its parent openacademy_menu and *after* its
         action course_list_action --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"courses_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Courses"</span> <span class="hljs-attr">parent</span>=<span class="hljs-string">"openacademy_menu"</span>
              <span class="hljs-attr">action</span>=<span class="hljs-string">"course_list_action"</span>/&gt;</span>
    <span class="hljs-comment">&lt;!-- Full id location:
         action="openacademy.course_list_action"
         It is not required when it is the same module --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>

</odoo>

  • 練習使用XML定制窗體視圖

建立課程對象的表單視圖,顯示課程的名稱和描述字段。

  • 練習notebook結構元素

在課程的表單視圖中,將描述字段放在一個選項卡中,然后再添加選項卡放置其它字段。修改后的課程表單視圖如下:

openacademy/views/openacademy.xml

  <!--form視圖 Course-->
        <record model="ir.ui.view" id="view_form_openacademy_course">
            <field name="name">course.form</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <form string="Course Form">
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="description"/>
                        </group>
                        <!--notebook標簽頁-->
                        <notebook>
                            <!--描述-->
                            <page string="Description">
                                <field name="description"/>
                            </page>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">notebook</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">sheet</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span></code></pre>

notebook效果:

當然也可以用純HTML頁面寫

  • 練習搜索課程

通過標題和描述來搜索課程。

   <!--搜索視圖 Course-->
        <record model="ir.ui.view" id="course_search_view">
            <field name="name">course.search</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <search>
                    <field name="name"/>
                    <field name="description"/>
                    <filter name="my_courses" string="My Courses" domain="[('responsible_id', '=', uid)]"/>
                    <group string="Group By">
                        <filter name="by_responsible" string="Responsible" context="{'group_by': 'responsible_id'}"/>
                    </group>
                </search>
            </field>
        </record>
  • 練習建立一個授課模型

在開放學院模塊中,我們考慮一個授課模型:一個授課是在給定的時間中對給定的受眾教授指定的課程。為授課建立模型,授課包括名稱、開始時間、持續時間和席位數。添加操作和菜單項來顯示新的模型。

openacademy/models.py

class Session(models.Model): _name = 'openacademy.session' 
name = fields.<span class="hljs-built_in">Char</span>(required=<span class="hljs-literal">True</span>)
start_date = fields.<span class="hljs-built_in">Date</span>()
duration = fields.Float(digits=(<span class="hljs-number">6</span>, <span class="hljs-number">2</span>), help=<span class="hljs-string">"Duration in days"</span>)
seats = fields.<span class="hljs-built_in">Integer</span>(<span class="hljs-built_in">string</span>=<span class="hljs-string">"Number of seats"</span>)

openacademy/views/openacademy.xml

<!-- session form view -->
        <record model="ir.ui.view" id="session_form_view">
            <field name="name">session.form</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <form string="Session Form">
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="start_date"/>
                            <field name="duration"/>
                            <field name="seats"/>
                        </group>
                    </sheet>
                </form>
            </field>
        </record>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.act_window"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"session_list_action"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Sessions<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"res_model"</span>&gt;</span>openacademy.session<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_type"</span>&gt;</span>form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_mode"</span>&gt;</span>tree,form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"session_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Sessions"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"openacademy_menu"</span>
              <span class="hljs-attr">action</span>=<span class="hljs-string">"session_list_action"</span>/&gt;</span>

  • 練習Many2one關聯

編輯Course和Session模型以反映他們與其它模型的關聯:

  • 課程有一個負責的用戶;該字段的值是內置模型res.users的記錄
#class Course(models.Model)
  responsible_id = fields.Many2one('res.users',
                                     ondelete='set null', string="Responsible", index=True)
  • 一個授課有一個教師;該字段的值是內置模型res.partner的記錄
#class Session(models.Model)
instructor_id = fields.Many2one('res.partner', string="Instructor")
  • 授課與課程相關;該字段的值是openacademy.course模型的記錄,並且是必填項
#class Session(models.Model)
course_id = fields.Many2one('openacademy.course',
                                ondelete='cascade', string="Course", required=True)
  • 在模型中添加Many2one關聯,並在視圖顯示
#form視圖
        <record model="ir.ui.view" id="view_form_openacademy_course">
            <field name="name">course.form</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <form string="Course Form">
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="description"/>
                            <field name="responsible_id"/>
                        </group>
                        <!--notebook標簽頁-->
                        <notebook>
                            <!--描述-->
                            <page string="Description">
                                <field name="description"/>
                            </page>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">notebook</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">sheet</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span></code></pre>

 <record model="ir.ui.view" id="course_tree_view">
            <field name="name">course.tree</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <tree string="Course Tree">
                    <field name="name"/>
                    <field name="description"/>
                    <field name="responsible_id"/>
                </tree>
            </field>
        </record>

多對一關系

關聯字段

Many2one(other_model, ondelete='set null')

多對一字段

一個鏈接到其它對象的簡單示例是這樣的:

print foo.other_id.name

一對多模型,通過for循環遍歷

這是一個虛擬的關聯,是Many2one的逆,One2many作為記錄的容器,訪問它將返回一個記錄集(也可能是一個空記錄集):

for other in foo.other_ids:
    print other.name
  • 練習逆關聯One2many
    使用逆關聯字段one2many,編輯模型以反映課程和授課之間的關系。

  • 編輯Course類,並且加入字段到它的表單視圖

#class Course(models.Model)
    session_ids = fields.One2many(
        'openacademy.session', 'course_id', string="Sessions")
    <!--form視圖 Course-->
             <record model="ir.ui.view" id="course_form_view">
            <field name="name">course.form</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <form string="Course Form">
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="responsible_id"/>
                        </group>
                        <!--notebook標簽頁-->
                        <notebook>
                            <!--描述-->
                            <page string="Description">
                                <field name="description"/>
                            </page>
                            <!--域-->
                            <page string="Sessions">
                                <field name="session_ids">
                                    <tree string="Registered sessions">
                                        <field name="name"/>
                                        <!--instructor_id授課教師與課程的捆綁-->
                                        <field name="instructor_id"/>
                                    </tree>
                                </field>
                            </page>
                        </notebook>
                    </sheet>
                </form>
            </field>
        </record>

notebook中添加了session

  • 練習多對多關聯many2many
    在授課模型中添加關聯字段many2many,將每次授課和參與的聽眾做關聯,聽眾來自於內置模型res.partner。相應的調整對應的視圖。

  • 修改Session類並且加入字段到它的表單視圖中
# class Session(models.Model)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

視圖

  <record model="ir.ui.view" id="session_form_view">
            <field name="name">session.form</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <form string="Session Form">
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="start_date"/>
                            <field name="duration"/>
                            <field name="seats"/>
                        </group>
                        <!--授課和參與的聽眾做關聯,聽眾來自於內置模型res.partner-->
                        <label for="attendee_ids"/>
                        <field name="attendee_ids"/>
                    </sheet>
                </form>
            </field>
        </record>

  • 練習更改現有內容

  • 使用模型繼承,修改現有partner模型,添加instructor布爾字段,以及對應表示"授課-講師"關聯的many2many字段
  • 使用視圖繼承在partner的表單視圖中顯示這個字段

注意,這里是通過開發人員模式來查找視圖外部ID並放置新字段的。

  • 創建文件openacademy/models/partner.py並將其導入__init__.py
  • 創建文件openacademy/views/partner.xml並將其添加到__manifest__.py

res.partner表的路徑

\odoo\odoo10\odoo\addons\base\res\res_partner.py

\odoo\odoo10\odoo\addons\product\views\res_partner_views.xml

openacademy/models/init.py

from . import models
from . import partner

openacademy/manifest.py

    # always loaded
    'data': [
        # 'security/ir.model.access.csv',
        'views/openacademy.xml',
        'views/templates.xml',
        'views/partner.xml',
        'demo/demo.xml',
    ],

openacademy/partner.py

# -*- coding: utf-8 -*- from odoo import fields, models 

class Partner(models.Model):
_inherit = 'res.partner'

<span class="hljs-comment"># Add a new column to the res.partner model, by default partners are not</span>
<span class="hljs-comment"># instructors</span>
instructor = fields.Boolean(<span class="hljs-string">"Instructor"</span>, default=<span class="hljs-keyword">False</span>)

session_ids = fields.Many2many(<span class="hljs-string">'openacademy.session'</span>,
    string=<span class="hljs-string">"Attended Sessions"</span>, readonly=<span class="hljs-keyword">True</span>)

openacademy/views/partner.xml

<?xml version="1.0" encoding="utf-8" ?>
<odoo>
    <data>
        <!-- Add instructor field to existing view -->
        <!--form視圖-->
        <record model="ir.ui.view" id="partner_instructor_form_view">
            <field name="name">partner.instructor</field>
            <field name="model">res.partner</field>
            <field name="inherit_id" ref="base.view_partner_form"/>
            <field name="arch" type="xml">
                <notebook position="inside">
                    <page string="Sessions">
                        <group>
                            <field name="instructor"/>
                            <field name="session_ids"/>
                        </group>
                    </page>
                </notebook>
            </field>
        </record>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.act_window"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_list_action"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Contacts<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"res_model"</span>&gt;</span>res.partner<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_mode"</span>&gt;</span>tree,form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"configuration_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Configuration"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"main_openacademy_menu"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Contacts"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"configuration_menu"</span>
              <span class="hljs-attr">action</span>=<span class="hljs-string">"contact_list_action"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.act_window"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_cat_list_action"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Contact Tags<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"res_model"</span>&gt;</span>res.partner.category<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_mode"</span>&gt;</span>tree,form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_cat_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Contact Tags"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"configuration_menu"</span>
              <span class="hljs-attr">action</span>=<span class="hljs-string">"contact_cat_list_action"</span>/&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"res.partner.category"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"teacher1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Teacher / Level 1<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"res.partner.category"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"teacher2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Teacher / Level 2<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>

</odoo>

<field name="inherit_id" ref="base.view_partner_form"/>視圖繼承
\odoo\odoo10\odoo\addons\product\views\res_partner_views.xml

  • 練習在關聯字段上使用Domain,
    當為授課選取講師時,只有instructor值為True的講師會被顯示出來。

注意
聲明為文字列表的domain會在服務端進行計算,不會出現在右側的動態列表中,而聲明為字符串的domain是在客戶端進行計算的,字段名將出現在右側列表。

#class Session(models.Model)
instructor_id = fields.Many2one('res.partner', string="Instructor",
                                    domain=[('instructor', '=', True)])
  • 練習更復雜的domain,創建新的partner類別Techer/Level1和Techer/Level2.一個授課的教授人可以是講師或者任意級別的教師。
       <!-- Add instructor field to existing view -->
        <!--form視圖-->
        <record model="ir.ui.view" id="partner_instructor_form_view">
            <field name="name">partner.instructor</field>
            <field name="model">res.partner</field>
            <!--繼承-->
            <field name="inherit_id" ref="base.view_partner_form"/>
            <field name="arch" type="xml">
                <notebook position="inside">
                    <page string="Sessions">
                        <group>
                            <field name="instructor"/>
                            <field name="session_ids"/>
                        </group>
                    </page>
                </notebook>
            </field>
        </record>
        <!--定義行為-->
        <record model="ir.actions.act_window" id="contact_list_action">
            <field name="name">Contacts</field>
            <field name="res_model">res.partner</field>
            <field name="view_mode">tree,form</field>
        </record>
        <!--菜單-->
        <menuitem id="configuration_menu" name="Configuration" parent="main_openacademy_menu"/>
        <menuitem id="contact_menu" name="Contacts" parent="configuration_menu" action="contact_list_action"/>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.act_window"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_cat_list_action"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Contact Tags<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"res_model"</span>&gt;</span>res.partner.category<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_mode"</span>&gt;</span>tree,form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>


    <span class="hljs-tag">&lt;<span class="hljs-name">menuitem</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact_cat_menu"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Contact Tags"</span>
              <span class="hljs-attr">parent</span>=<span class="hljs-string">"configuration_menu"</span>
              <span class="hljs-attr">action</span>=<span class="hljs-string">"contact_cat_list_action"</span>/&gt;</span>
    <span class="hljs-comment">&lt;!-- 定義分類--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"res.partner.category"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"teacher1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Teacher / Level 1<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"res.partner.category"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"teacher2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Teacher / Level 2<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span></code></pre>

視圖繼承路徑\odoo\odoo10\odoo\addons\product\views\res_partner_views.xml

  • 練習計算字段

  • 加入座席占用百分比字段到授課模型。
  • 在列表視圖和表單視圖中顯示這個字段
  • 以進度條的方式顯示這個字段

#class Session(models.Model) 

taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')

@api.depends(<span class="hljs-string">'seats'</span>, <span class="hljs-string">'attendee_ids'</span>)   <span class="hljs-comment">#依賴的任一字段變化時(ORM or Form),觸發該函數執行</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_taken_seats</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
    <span class="hljs-keyword">for</span> r <span class="hljs-keyword">in</span> <span class="hljs-symbol">self:</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> r.<span class="hljs-symbol">seats:</span>
            r.taken_seats = <span class="hljs-number">0</span>.<span class="hljs-number">0</span>
        <span class="hljs-symbol">else:</span>
            r.taken_seats = <span class="hljs-number">100.0</span> * len(r.attendee_ids) / r.seats

<field name="taken_seats" widget="progressbar"/>progresbar進度條

  • 練習默認值

定義start_date默認值為今天
在授課類添加字段active,並且設置其默認值為True

class Session(models.Model): start_date = fields.Date(default=fields.Date.today) active = fields.Boolean(default=True)

注意
Odoo 有內置規則:active字段值為False時記錄不可見

  • 練習
    通過"onchange"機制顯示的驗證無效值,例如座位數為負數或者座位數多與參與者。
#class Session(models.Model)
@api.onchange('seats', 'attendee_ids')
    def _verify_valid_seats(self):
        if self.seats < 0:
            return {
                'warning': {
                    'title': "Incorrect 'seats' value",
                    'message': "The number of available seats may not be negative",
                },
            }
        if self.seats < len(self.attendee_ids):
            return {
                'warning': {
                    'title': "Too many attendees",
                    'message': "Increase seats or remove excess attendees",
                },
            }

  • 練習添加Python約束,講師不能在自己的授課出席人中
import exceptions

#class Session(models.Model):
@api.constrains('instructor_id', 'attendee_ids') #講師不能在自己的授課出席人中
def _check_instructor_not_in_attendees(self):
for r in self:
if r.instructor_id and r.instructor_id in r.attendee_ids:
raise exceptions.ValidationError("A session's instructor can't be an attendee")

  • 練習添加重復項,因為我們為課程名稱添加了唯一性約束,所以不能再使用"復制"功能(表單->復制)。重寫"復制"方法,允許復制課程對象,將原始名稱更改為"原始名稱的副本"。
#class Course(models.Model)
 @api.multi
    def copy(self, default=None):
        default = dict(default or {})
        print '--------------'
        print self.name
        print '--------------'
        copied_count = self.search_count(
            [('name', '=like', u"Copy of {}%".format(self.name))])
        if not copied_count:
            new_name = u"Copy of {}".format(self.name)
        else:
            new_name = u"Copy of {} ({})".format(self.name, copied_count)
    default[<span class="hljs-string">'name'</span>] = new_name
    <span class="hljs-keyword">return</span> super(Course, self).copy(default)</code></pre>
    <record model="ir.ui.view" id="session_form_view">
            <field name="name">session.form</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <form string="Session Form">
                    <!--<sheet>-->
                        <!--<group>-->
                            <!--<field name="course_id"/>-->
                            <!--<field name="name"/>-->
                            <!--<field name="start_date"/>-->
                            <!--<field name="duration"/>-->
                            <!--<field name="seats"/>-->
                            <!--<field name="instructor_id"/>-->
                        <!--</group>-->
                        <!--&lt;!&ndash;授課和參與的聽眾做關聯,聽眾來自於內置模型res.partner&ndash;&gt;-->
                        <!--<label for="attendee_ids"/>-->
                        <!--<field name="attendee_ids"/>-->
                    <!--</sheet>-->
                    <sheet>
                        <group>
                            <group string="General">
                                <field name="course_id"/>
                                <field name="name"/>
                                <field name="instructor_id"/>
                                <field name="active"/>
                            </group>
                            <group string="Schedule">
                                <field name="start_date"/>
                                <!--<field name="end_date"/>-->
                                <field name="duration"/>
                                <!--<field name="hours"/>-->
                                <field name="seats"/>
                                <field name="taken_seats" widget="progressbar"/>
                            </group>
                        </group>
                        <label for="attendee_ids"/>
                        <field name="attendee_ids"/>
                    </sheet>
                </form>
            </field>
        </record>

打印出來的name,default

--------------
語文
--------------
{'name': u'Copy of \u8bed\u6587'}

-----

高級視圖

  • 練習列表着色
    編輯授課的樹視圖,使得持續時間少於5天的授課以藍色顯示,持續時間超過15天的授課以紅色顯示。編輯授課的樹視圖:
 <record model="ir.ui.view" id="session_tree_view">
            <field name="name">session.tree</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <tree string="Session Tree" decoration-info="duration&lt;5" decoration-danger="duration&gt;15">
                    <field name="name"/>
                    <field name="course_id"/>
                    <field name="duration" invisible="1"/>
                    <field name="taken_seats" widget="progressbar"/>
                    <field name="state"/>
                </tree>
            </field>
        </record>

  • 練習日歷視圖
    給授課模型添加一個日歷視圖,使用戶可以查看與開放學院相關聯的事件。

  • 添加一個計算字段end_date,通過start_dateduration計算獲得。
  • 反函數使字段可寫,並允許在日歷視圖中移動授課(通過拖放操作)
  • 向授課模型添加日歷視圖
  • 添加日歷視圖到授課模型的動作中

models.py

from datetime import timedelta # class Session(models.Model) 

end_date = fields.Date(string="End Date", store=True,
compute='_get_end_date', inverse='_set_end_date') #結束日期

@api.depends('start_date', 'duration') #計算end_date結束日期
def _get_end_date(self):
for r in self:
if not (r.start_date and r.duration):
r.end_date = r.start_date
continue

        <span class="hljs-comment"># Add duration to start_date, but: Monday + 5 days = Saturday, so</span>
        <span class="hljs-comment"># subtract one second to get on Friday instead</span>
        start = fields.Datetime.from_string(r.start_date)
        duration = timedelta(days=r.duration, seconds=-<span class="hljs-number">1</span>)
        r.end_date = start + duration

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_set_end_date</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
    <span class="hljs-keyword">for</span> r <span class="hljs-keyword">in</span> <span class="hljs-symbol">self:</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (r.start_date <span class="hljs-keyword">and</span> r.end_date):
            continue

        <span class="hljs-comment"># Compute the difference between dates, but: Friday - Monday = 4 days,</span>
        <span class="hljs-comment"># so add one day to get 5 days instead</span>
        start_date = fields.Datetime.from_string(r.start_date)
        end_date = fields.Datetime.from_string(r.end_date)
        r.duration = (end_date - start_date).days + <span class="hljs-number">1</span>

  • 練習搜索視圖

  • 在課程搜索視圖中添加按鈕,用以篩選當前用戶負責的課程,並且作為默認選擇。
  • 再添加一個分組按鈕,用於對當前用戶負責的課程進行分組。

  <!--搜索視圖 Course-->
        <record model="ir.ui.view" id="course_search_view">
            <field name="name">course.search</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <search>
                    <field name="name"/>
                    <field name="description"/>
                    <filter name="my_courses" string="My Courses" domain="[('responsible_id', '=', uid)]"/>
                    <group string="Group By">
                        <filter name="by_responsible" string="Responsible" context="{'group_by': 'responsible_id'}"/>
                    </group>
                </search>
            </field>
        </record>

<!--動作定義-->
<record model="ir.actions.act_window" id="course_list_action">
<field name="name">Courses</field>
<field name="res_model">openacademy.course</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<!--在課程搜索視圖中添加按鈕,用以篩選當前用戶負責的課程,並且作為默認選擇 默認選擇的規則-->
<field name="context" eval="{'search_default_my_courses': 1}"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first course
</p>
</field>
</record>

  • 練習甘特圖

添加甘特圖使用戶可以查看授課的日程排期,授課將按講師分組。

  • 創建一個計算字段,表示以小時計算的授課持續時間
  • 添加甘特圖,並且將甘特圖添加到授課模型的action上。
   <!--甘特圖-->
      <record model="ir.ui.view" id="session_gantt_view">
            <field name="name">session.gantt</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <gantt string="Session Gantt" color="course_id" date_start="start_date" date_delay="hours" default_group_by='instructor_id'>
                    <field name="name"/>
                </gantt>
            </field>
        </record>

  • 練習圖形視圖
    在授課對象中添加圖形視圖,為每個課程在條形視圖下顯示出席人數。

  • 添加字段將出席人數這計算字段存儲在數據庫
  • 添加相關圖形視圖

<!--圖形視圖-->
        <record model="ir.ui.view" id="openacademy_session_graph_view">
            <field name="name">openacademy.session.graph</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <graph string="Participations by Courses">
                    <field name="course_id"/>
                    <field name="attendees_count" type="measure"/>
                </graph>
            </field>
        </record>

  • 練習看板視圖
    添加顯示按課程分組的授課看板視圖(列是課程)

  • 授課模型中添加整型字段color
  • 添加看板視圖並更新action
color = fields.Integer()
 <!--看板視圖--> <record model="ir.ui.view" id="view_openacad_session_kanban"> <field name="name">openacad.session.kanban</field> <field name="model">openacademy.session</field> <field name="arch" type="xml"> <kanban default_group_by="course_id"> <field name="color"/> <templates> <t t-name="kanban-box"> <div t-attf-class="oe_kanban_color_{{kanban_getcolor(record.color.raw_value)}} oe_kanban_global_click_edit oe_semantic_html_override oe_kanban_card {{record.group_fancy==1 ? 'oe_kanban_card_fancy' : ''}}"> <div class="oe_dropdown_kanban"> <!-- dropdown menu --> <div class="oe_dropdown_toggle"> <i class="fa fa-bars fa-lg"/> <ul class="oe_dropdown_menu"> <li> <a type="delete">Delete</a> </li> <li> <ul class="oe_kanban_colorpicker" data-field="color"/> </li> </ul> </div> <div class="oe_clear"></div> </div> <div t-attf-class="oe_kanban_content"> <!-- title --> Session name: <field name="name"/> <br/> Start date: <field name="start_date"/> <br/> duration: <field name="duration"/> </div> </div> </t> </templates> </kanban> </field> </record> 
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.act_window"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"session_list_action"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Sessions<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"res_model"</span>&gt;</span>openacademy.session<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_type"</span>&gt;</span>form<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"view_mode"</span>&gt;</span>tree,form,calendar,gantt,graph,kanban<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span></span></code></pre>

----

工作流和安全

  • 練習偽工作流

在授課模型上添加一個字段state,用於定義一個工作流程。授課存在三個可能的狀態:Draft(草稿,默認值)、Confirmed(已確認)、Done(已完成)。在授課的form視圖中,添加一個只讀字段用於顯示課程狀態,並可以通過按鈕來改變狀態。有效的狀態值遷移包括:

  • Draft->Confirmed
  • Confirmed->Draft
  • Confirmed->Done
  • Done->Draft
  1. 添加一個新的字段state
  2. 添加狀態遷移方法,這個方法可以被form表單的按鈕所調用,用以更改授課的狀態
  3. 將相關按鈕添加到授課的form視圖
  4. openacademy/models.py
#class Session(models.Model):
 state = fields.Selection([
        ('draft', "Draft"),
        ('confirmed', "Confirmed"),
        ('done', "Done"),
    ], default='draft')   #工作流狀態
<odoo> <data> <!--工作流--> <record model="workflow" id="wkf_session"> <field name="name">OpenAcademy sessions workflow</field> <field name="osv">openacademy.session</field> <field name="on_create">True</field> </record> 
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.server"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"set_session_to_draft"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Set session to Draft<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"code"</span>&gt;</span>
        model.search([('id', 'in', context['active_ids'])]).action_draft()
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>


    <span class="hljs-comment">&lt;!--工作流活動--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"workflow.activity"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"draft"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Draft<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"wkf_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"wkf_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"flow_start"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"True"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"kind"</span>&gt;</span>dummy<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"set_session_to_draft"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>

    <span class="hljs-comment">&lt;!--服務器--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.server"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"set_session_to_confirmed"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Set session to Confirmed<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"code"</span>&gt;</span>
        model.search([('id', 'in', context['active_ids'])]).action_confirm()
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>


    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"workflow.activity"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"confirmed"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Confirmed<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"wkf_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"wkf_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"kind"</span>&gt;</span>dummy<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"set_session_to_confirmed"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.server"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"set_session_to_done"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Set session to Done<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"code"</span>&gt;</span>
        model.search([('id', 'in', context['active_ids'])]).action_done()
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"workflow.activity"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"done"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Done<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"wkf_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"wkf_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"kind"</span>&gt;</span>dummy<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"action_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"set_session_to_done"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>

<!-- 工作流流轉 -->
<record model="workflow.transition" id="session_draft_to_confirmed">
<field name="act_from" ref="draft"/>
<field name="act_to" ref="confirmed"/>
<field name="signal">confirm</field>
</record>
<record model="workflow.transition" id="session_confirmed_to_draft">
<field name="act_from" ref="confirmed"/>
<field name="act_to" ref="draft"/>
<field name="signal">draft</field>
</record>
<record model="workflow.transition" id="session_done_to_draft">
<field name="act_from" ref="done"/>
<field name="act_to" ref="draft"/>
<field name="signal">draft</field>
</record>
<record model="workflow.transition" id="session_confirmed_to_done">
<field name="act_from" ref="confirmed"/>
<field name="act_to" ref="done"/>
<field name="signal">done</field>
</record>

    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"workflow.transition"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"session_auto_confirm_half_filled"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"act_from"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"draft"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"act_to"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"confirmed"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"condition"</span>&gt;</span>taken_seats &amp;gt; 50<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>

</odoo>

  • 練習工作流
    使用真正的授課工作流替換之前的偽工作流。修改授課的form視圖,按鈕將調用工作流而不是調用模型的方法。

  • 練習自動狀態遷移
    當超過一半座席被保留時,自動將授課的狀態從Draft遷移到Confirmed。

當定義了工作流,需要卸載模塊,否則原來定義好的數據無法進入工作流(可以直接修改數據庫)

<record model="workflow.transition" id="session_auto_confirm_half_filled">
            <field name="act_from" ref="draft"/>
            <field name="act_to" ref="confirmed"/>
            <field name="condition">taken_seats > 50</field>
        </record>
    </data>
</odoo>

  • 練習服務器動作
    用服務器動作替換用於同步授課狀態的Python方法。工作流和服務器動作都可以從UI創建。
<record model="ir.actions.server" id="set_session_to_draft">
            <field name="name">Set session to Draft</field>
            <field name="model_id" ref="model_openacademy_session"/>
            <field name="code">
model.search([('id', 'in', context['active_ids'])]).action_draft()
            </field>
        </record>
        <record model="workflow.activity" id="draft">
            <field name="name">Draft</field>
            <field name="wkf_id" ref="wkf_session"/>
            <field name="flow_start" eval="True"/>
            <field name="kind">dummy</field>
            <field name="action"></field>
            <field name="action_id" ref="set_session_to_draft"/>
        </record>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.server"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"set_session_to_confirmed"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Set session to Confirmed<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"code"</span>&gt;</span>

model.search([('id', 'in', context['active_ids'])]).action_confirm()
</field>
</record>
<record model="workflow.activity" id="confirmed">
<field name="name">Confirmed</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">dummy</field>
<field name="action"></field>
<field name="action_id" ref="set_session_to_confirmed"/>
</record>

    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.actions.server"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"set_session_to_done"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Set session to Done<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_session"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"code"</span>&gt;</span>

model.search([('id', 'in', context['active_ids'])]).action_done()
</field>
</record>
<record model="workflow.activity" id="done">
<field name="name">Done</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">dummy</field>
<field name="action"></field>
<field name="action_id" ref="set_session_to_done"/>
</record>

  • 練習
    通過Odoo界面添加訪問控制權限
    建立一個新用戶John Smit,然后建立OpenAcademy/Session Read組,並賦予這個組對授課模型的讀權限。
  1. 建立一個新用戶John Smit通過 設置->用戶->用戶
  2. 建立一個新組session_read通過 設置->用戶->組,這個組擁有對授課模型的讀權限
  3. 編輯John Smith用戶,把他加入到session_read
  4. John Smith身份登錄系統,檢查權限是否正確。

  • 練習
    通過數據文件添加訪問控制權限:

  • 建立一個組OpenAcademy / Manager,這個組對開放學院的所有模型都有完全權限。
  • 讓Session和Course對所有用戶可讀
  1. 建立新的文件openacademy/security/security.xml用來定義OpenAcademy Manager組
  2. 編輯文件openacademy/security/ir.model.access.csv來添加對模型的訪問權限
  3. 最后更新openacademy/manifest.py來添加新的數據文件

openacademy/__manifest__.py

注意security.xml順序,優先加載security.xml 然后加載csv,否則報錯

Exception: Module loading openacademy failed: file openacademy/security/ir.model.access.csv could not be processed:
 在字段'Group'中沒找到匹配的記錄外部id 'group_manager'
在字段'Group'中沒找到匹配的記錄外部id 'group_manager'
在字段'Object'中沒找到匹配的記錄外部id 'model_scheduler_demo'
在字段'Group'中沒找到匹配的記錄外部id 'group_manager'
  'data': [
        'security/security.xml',
        'security/ir.model.access.csv',
        'views/openacademy.xml',
        'views/templates.xml',
        'views/partner.xml',
        'views/session_workflow.xml',
        'demo/demo.xml',
    ],

openacademy/security/ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
course_read_all,course all,model_openacademy_course,,1,0,0,0
session_read_all,session all,model_openacademy_session,,1,0,0,0

openacademy/security/security.xml

<odoo>
    <data>
        <record id="group_manager" model="res.groups">
            <field name="name">OpenAcademy / Manager</field>
        </record>
    </data>
</odoo>

只能可讀,不可修改

  • 練習記錄規則
    為授課模型和OpenAcademy / Manager組添加記錄規則,這個記錄規則限制只有課程負責人可以對課程進行write和unlink操作,如果課程還沒有負責人,這個組的所有用戶都可以編輯它。在openacademy/security/security.xml文件中創建新的規則:

openacademy/security/security.xml

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <data>
        <record id="group_manager" model="res.groups">
            <field name="name">OpenAcademy / Manager</field>
        </record>
    <span class="hljs-tag">&lt;<span class="hljs-name">record</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"only_responsible_can_modify"</span> <span class="hljs-attr">model</span>=<span class="hljs-string">"ir.rule"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>&gt;</span>Only Responsible can modify Course<span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"model_id"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"model_openacademy_course"</span>/&gt;</span>  #模型課程分組
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"groups"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"[(4, ref('openacademy.group_manager'))]"</span>/&gt;</span>  #   (4,id,_) 連接一個已經存在的記錄
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"perm_read"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"0"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"perm_write"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"1"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"perm_create"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"0"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"perm_unlink"</span> <span class="hljs-attr">eval</span>=<span class="hljs-string">"1"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">field</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"domain_force"</span>&gt;</span>
            ['|', ('responsible_id','=',False),
                  ('responsible_id','=',user.id)]
        <span class="hljs-tag">&lt;/<span class="hljs-name">field</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">record</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>

</odoo>

只有添加到用戶組里面才可以進行讀寫



向導與國際化
-------

  • 練習定義向導
    創建一個向導模型,這個向導模型通過many2one關聯授課模型,並通過many2many關聯合作伙伴模型。添加新文件openacademy/wizard.py

openacademy/models/__init__.py

from . import models
from . import partner
from . import wizard

openacademy/wizard.py

# -*- coding: utf-8 -*-

from odoo import models, fields, api

class Wizard(models.TransientModel):
_name = 'openacademy.wizard'

session_ids = fields.Many2one(<span class="hljs-string">'openacademy.session'</span>,
    string=<span class="hljs-string">"Session"</span>, required=<span class="hljs-keyword">True</span>)
attendee_ids = fields.Many2many(<span class="hljs-string">'res.partner'</span>, string=<span class="hljs-string">"Attendees"</span>)

  • 練習啟動向導
  1. 為向導定義一個form視圖
  2. 在授課模型的上下文中添加action用於啟動向導
  3. 給向導的session字段定義默認值;使用上下文參數self._context來獲取當前授課

openacademy/wizard.py

class Wizard(models.TransientModel):
    _name = 'openacademy.wizard'
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_default_session</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.env[<span class="hljs-string">'openacademy.session'</span>].browse(<span class="hljs-keyword">self</span>._context.get(<span class="hljs-string">'active_id'</span>))

session_ids = fields.Many2one(<span class="hljs-string">'openacademy.session'</span>,
    string=<span class="hljs-string">"Session"</span>, required=True, default=_default_session)
attendee_ids = fields.Many2many(<span class="hljs-string">'res.partner'</span>, string=<span class="hljs-string">"Attendees"</span>)

openacademy/views/openacademy.xml

<!--定義向導視圖-->
       <record model="ir.ui.view" id="wizard_form_view">
            <field name="name">wizard.form</field>
            <field name="model">openacademy.wizard</field>
            <field name="arch" type="xml">
                <form string="Add Attendees">
                    <group>
                        <field name="session_ids"/>
                        <field name="attendee_ids"/>
                    </group>
                    <!--底部-->
                    <footer>
                        <!--訂閱按鈕-->
                        <button name="subscribe" type="object" string="Subscribe" class="oe_highlight"/>
                        <!--取消按鈕-->
                        <button special="cancel" string="Cancel"/>
                    </footer>
                </form>
            </field>
        </record>
  <span class="hljs-comment">&lt;!--定義向導行為--&gt;</span>
  <span class="hljs-comment">&lt;!--關聯session   定義wizard--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">act_window</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"launch_session_wizard"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"Add Attendees"</span>
                <span class="hljs-attr">src_model</span>=<span class="hljs-string">"openacademy.session"</span>
                <span class="hljs-attr">res_model</span>=<span class="hljs-string">"openacademy.wizard"</span>
                <span class="hljs-attr">view_mode</span>=<span class="hljs-string">"form"</span>
                <span class="hljs-attr">target</span>=<span class="hljs-string">"new"</span>
                <span class="hljs-attr">key2</span>=<span class="hljs-string">"client_action_multi"</span>/&gt;</span></code></pre>

  • 練習注冊與會者
    給向導添加按鈕,並且實現相應的方法,將與會者添加到給定的授課。
 <footer>
                        <!--訂閱按鈕-->
                        <button name="subscribe" type="object" string="Subscribe" class="oe_highlight"/>
                        <!--取消按鈕-->
                        <button special="cancel" string="Cancel"/>
                    </footer>
  • 練習與會者注冊多個授課
    修改向導模型,以便與會者可以注冊到多個授課
class Wizard(models.TransientModel): _name = 'openacademy.wizard' 
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_default_sessions</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.env[<span class="hljs-string">'openacademy.session'</span>].browse(<span class="hljs-keyword">self</span>._context.get(<span class="hljs-string">'active_ids'</span>))
    print <span class="hljs-string">'------------------------'</span>
    print dir(<span class="hljs-keyword">self</span>)
    print <span class="hljs-string">'------------------------'</span>
<span class="hljs-comment">#字段</span>
session_ids = fields.Many2one(<span class="hljs-string">'openacademy.session'</span>,
    string=<span class="hljs-string">"Session"</span>, required=True)
attendee_ids = fields.Many2many(<span class="hljs-string">'res.partner'</span>, string=<span class="hljs-string">"Attendees"</span>)


@api.multi
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>:
    <span class="hljs-keyword">for</span> session <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.<span class="hljs-symbol">session_ids:</span>
        session.attendee_ids <span class="hljs-params">|= <span class="hljs-keyword">self</span>.attendee_ids
    <span class="hljs-keyword">return</span> {}

  • 練習
    翻譯一個模塊
    為已經安裝的Odoo模塊選擇第二語言。使用Odoo提供的功能對模塊進行翻譯。
  1. 創建目錄openacademy/i18n/
  2. 安裝任意一種你希望的語言 (設置->翻譯->加載翻譯
  3. 同步翻譯術語(設置->翻譯->應用程序術語->同步術語
  4. 導出不指定語言的翻譯模板文件(設置->翻譯->導入/導出->導出翻譯),保存在openacademy/i18n/
  5. 導出指定語言的翻譯文件(設置->翻譯->導入/導出->導出翻譯),保存在openacademy/i18n/
  6. 打開導出的翻譯文件(使用任意一款文本編輯軟件或者專用的PO文件編輯器,例如POEdit然后翻譯其中的術語)
  7. models.py文件中,為odoo._方法添加一個導入聲明,並且標記需要翻譯的字符串
  8. 重復3-6的步驟

實際上添加i18n這個文件夾,放入導出的po文件zh_cn.po文件,必須重新安裝模塊后,整個翻譯才會生效

openacademy/models.py

下划線翻譯相應的瀏覽器提示

from datetime import timedelta
from odoo import models, fields, api, exceptions, _

class Course(models.Model):
_name = 'openacademy.course'

default = dict(default or {})

        copied_count = self.search_count(
            [('name', '=like', _(u"Copy of {}%").format(self.name))])
        if not copied_count:
            new_name = _(u"Copy of {}").format(self.name)
        else:
            new_name = _(u"Copy of {} ({})").format(self.name, copied_count)

        default['name'] = new_name
        return super(Course, self).copy(default)


if self.seats < 0:
            return {
                'warning': {
                    'title': _("Incorrect 'seats' value"),
                    'message': _("The number of available seats may not be negative"),
                },
            }
        if self.seats < len(self.attendee_ids):
            return {
                'warning': {
                    'title': _("Too many attendees"),
                    'message': _("Increase seats or remove excess attendees"),
                },
            }

def _check_instructor_not_in_attendees(self):
        for r in self:
            if r.instructor_id and r.instructor_id in r.attendee_ids:
                raise exceptions.ValidationError(_("A session's instructor can't be an attendee"))

-----

報表和WebService

  • 練習創建授課模型的報表
    對於每個授課,報表都將顯示它的授課名稱,開始和結束時間,以及課程出席人列表。
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
    <data>
        <!--定義報表-->
         <report id="report_session" model="openacademy.session" string="Session Report" name="openacademy.report_session_view" file="openacademy.report_session" report_type="qweb-pdf" />
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"report_session_view"</span>&gt;</span>
        <span class="hljs-comment">&lt;!--調用子模板--&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">t</span> <span class="hljs-attr">t-call</span>=<span class="hljs-string">"report.html_container"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">t</span> <span class="hljs-attr">t-foreach</span>=<span class="hljs-string">"docs"</span> <span class="hljs-attr">t-as</span>=<span class="hljs-string">"doc"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">t</span> <span class="hljs-attr">t-call</span>=<span class="hljs-string">"report.external_layout"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page"</span>&gt;</span>
                        <span class="hljs-comment">&lt;!--只能用於格式化記錄字段--&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">t-field</span>=<span class="hljs-string">"doc.name"</span>/&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>From <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">t-field</span>=<span class="hljs-string">"doc.start_date"</span>/&gt;</span> to <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">t-field</span>=<span class="hljs-string">"doc.end_date"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Attendees:<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">t</span> <span class="hljs-attr">t-foreach</span>=<span class="hljs-string">"doc.attendee_ids"</span> <span class="hljs-attr">t-as</span>=<span class="hljs-string">"attendee"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">t-field</span>=<span class="hljs-string">"attendee.name"</span>/&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">t</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">t</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">t</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">t</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">data</span>&gt;</span>

</odoo>

  • 練習定義一個儀表盤
    定義一個儀表盤,這個儀表盤包含了已經建立的圖形視圖、授課日歷視圖、課程列表視圖(可以選擇form視圖)。這個儀表盤可以通過菜單中的菜單項使用,並且當選擇開放學院主菜單時自動顯示在web客戶端。
'version': '0.1',
<span class="hljs-comment"># any module necessary for this one to work correctly</span>
<span class="hljs-string">'depends'</span>: [<span class="hljs-string">'base'</span>, <span class="hljs-string">'board'</span>],

<span class="hljs-comment"># always loaded</span>
<span class="hljs-string">'data'</span>: [
    <span class="hljs-string">'views/openacademy.xml'</span>,
    <span class="hljs-string">'views/partner.xml'</span>,
    <span class="hljs-string">'views/session_workflow.xml'</span>,
    <span class="hljs-string">'views/session_board.xml'</span>,
    <span class="hljs-string">'reports.xml'</span>,
],

<?xml version="1.0"?>
<odoo>
    <data>
        <record model="ir.actions.act_window" id="act_session_graph">
            <field name="name">Attendees by course</field>
            <field name="res_model">openacademy.session</field>
            <field name="view_type">form</field>
            <field name="view_mode">graph</field>
            <field name="view_id" ref="openacademy.openacademy_session_graph_view"/>
        </record>
        <record model="ir.actions.act_window" id="act_session_calendar">
            <field name="name">Sessions</field>
            <field name="res_model">openacademy.session</field>
            <field name="view_type">form</field>
            <field name="view_mode">calendar</field>
            <field name="view_id" ref="openacademy.session_calendar_view"/>
        </record>
        <record model="ir.actions.act_window" id="act_course_list">
            <field name="name">Courses</field>
            <field name="res_model">openacademy.course</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
        </record>
        <record model="ir.ui.view" id="board_session_form">
            <field name="name">Session Dashboard Form</field>
            <field name="model">board.board</field>
            <field name="type">form</field>
            <field name="arch" type="xml">
                <form string="Session Dashboard">
                    <board style="2-1">
                        <column>
                            <action string="Attendees by course" name="%(act_session_graph)d" height="150" width="510"/>
                            <action string="Sessions" name="%(act_session_calendar)d"/>
                        </column>
                        <column>
                            <action string="Courses" name="%(act_course_list)d"/>
                        </column>
                    </board>
                </form>
            </field>
        </record>
        <record model="ir.actions.act_window" id="open_board_session">
          <field name="name">Session Dashboard</field>
          <field name="res_model">board.board</field>
          <field name="view_type">form</field>
          <field name="view_mode">form</field>
          <field name="usage">menu</field>
          <field name="view_id" ref="board_session_form"/>
        </record>

        <menuitem name="Session Dashboard" parent="base.menu_reporting_dashboard" action="open_board_session" sequence="1" id="menu_board_session" icon="terp-graph"/>
    </data>
</odoo>

  • 練習在客戶端添加新服務
    編寫Python程序,用來發送XML-RPC請求到運行Odoo的PC。這個程序將顯示全部授課,以及所對應的座位數。也可以為課程創建新的授課。
分類: odoo
標簽: odoo
好文要頂 關注成功 收藏該文
0
0
» 下一篇: odoo官方文檔第一章 ORM
	</div>
	<div class="postDesc">posted @ <span id="post-date">2018-07-06 23:19</span> <a href="https://www.cnblogs.com/myt2000/">苦苦思索的吃瓜群眾</a> 閱讀(<span id="post_view_count">145</span>) 評論(<span id="post_comment_count">0</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=9275891" rel="nofollow">編輯</a> <a href="#" onclick="AddToWz(9275891);return false;">收藏</a></div>
</div>
<script src="//common.cnblogs.com/highlight/9.12.0/highlight.min.js"></script><script>markdown_highlight();</script><script type="text/javascript">var allowComments=true,cb_blogId=442695,cb_entryId=9275891,cb_blogApp=currentBlogApp,cb_blogUserGuid='6031fa4c-c565-4e1f-9ae2-08d5866282ce',cb_entryCreatedDate='2018/7/6 23:19:00';loadViewCount(cb_entryId);var cb_postType=1;var isMarkdown=true;</script>
</div><!--end: forFlow -->
</div>
posted @ 2019-07-04 17:56  P_PlusUltra  閱讀( 458)  評論( 0編輯  收藏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM