odoo10.0 API 创建模板

来源:互联网 发布:阿里云 宽带 编辑:程序博客网 时间:2024/05/31 05:28

创建模板

 

警告

本教程需要安装Odoo

启动/停止Odoo服务器

Odoo使用客户端/服务器架构,客户端是通过RPC访问Odoo服务器的Web浏览器。

业务逻辑和扩展通常在服务器端执行,尽管可以向客户端添加支持客户端功能(如交互式地图等新数据表示)。

为了启动服务器,只需在shell中调用odoo-bin命令即可,如果需要,将完整路径添加到该文件中:

odoo- bin

通过Ctrl-C从终端击中两次或通过杀死相应的操作系统进程来停止服务器。

构建一个Odoo模块

服务器和客户端扩展都被打包为可选地加载到数据库中的模块

Odoo模块可以向Odoo系统添加全新的业务逻辑,也可以改变和扩展现有的业务逻辑:可以创建一个模块,将您所在国家的会计规则添加到Odoo的通用会计支持中,而下一个模块增加了对实时可视化的支持的公交车队。

Odoo中的所有内容都以模块开始和结束。

模块的组成

Odoo模块可以包含许多元素:

业务对象

这些资源被声明为Python类,这些资源将根据Odoo的配置自动保留

数据文件

声明元数据(视图或工作流),配置数据(模块参数化),演示数据等的XML或CSV文件

Web控制器

处理来自网络浏览器的请求

静态网页数据

Web界面或网站使用的图片,CSS或JavaScript文件

模块结构

每个模块都是模块目录中的一个目录。使用该--addons-path 选项指定模块目录。

小费

大多数命令行选项也可以使用配置文件进行设置

Odoo模块由其清单声明。请参阅清单文档

模块也是 Python __init__.py文件,其中包含用于将模块以各种Python文件导入说明。

例如,如果模块有单个mymodule.py文件__init__.py 可能包含:

from . import mymodule

Odoo提供了一种机制,以帮助建立一个新的模块,odoo有一个子支架创建一个空的模块:

$ odoo-bin scaffold <module name> <where to put it>

该命令为您的模块创建一个子目录,并为模块自动创建一堆标准文件。他们大多数只是包含注释的代码或XML。大多数这些文件的使用将在本教程中解释。

行使

模块创建

使用上面的命令行创建一个空模块Open Academy,并将其安装在Odoo中。

1. 调用命令odoo-bin scaffold openacademy addons

2. 将清单文件修改到模块。

3. 不要打扰其他文件。

openacademy/__manifest__.py

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

{    'name': "Open Academy", 

    'summary': """Manage trainings""",

'description': """

        Open Academy module for managing trainings:

            - training courses

            - training sessions

            - attendees registration

    """,

    'author': "My Company",

    'website': "http://www.yourcompany.com",

    # Categories can be used to filter modules in modules listing

    # Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml    

# for the full list    'category': 'Test',    'version': '0.1',

    # any module necessary for this one to work correctly

    'depends': ['base'],

    # always loaded

    'data': [

        # 'security/ir.model.access.csv',

        'templates.xml',    ],

    # only loaded in demonstration mode

    'demo': [

        'demo.xml', 

   ],}

openacademy/__init__.py

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

from . import controllers

from . import models

openacademy/controllers.py

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

from odoo import http

# class Openacademy(http.Controller):

#     @http.route('/openacademy/openacademy/', auth='public')

#     def index(self, **kw):

#         return "Hello, world"

#     @http.route('/openacademy/openacademy/objects/', auth='public')

#     def list(self, **kw):

#         return http.request.render('openacademy.listing', {

#             'root': '/openacademy/openacademy',

#             'objects': http.request.env['openacademy.openacademy'].search([]),

#         })

#     @http.route('/openacademy/openacademy/objects/<model("openacademy.openacademy"):obj>/', auth='public')

#     def object(self, obj, **kw):

#         return http.request.render('openacademy.object', {

#             'object': obj

#         })

openacademy/demo.xml

<odoo>

<data>

        <!--  -->

        <!--   <record id="object0" model="openacademy.openacademy"> -->

        <!--     <field name="name">Object 0</field> -->

        <!--   </record> -->

        <!--  -->

        <!--   <record id="object1" model="openacademy.openacademy"> -->

        <!--     <field name="name">Object 1</field> -->

        <!--   </record> -->

        <!--  -->

        <!--   <record id="object2" model="openacademy.openacademy"> -->

        <!--     <field name="name">Object 2</field> -->

        <!--   </record> -->

        <!--  -->

        <!--   <record id="object3" model="openacademy.openacademy"> -->

        <!--     <field name="name">Object 3</field> -->

        <!--   </record> -->

        <!--  -->

        <!--   <record id="object4" model="openacademy.openacademy"> -->

        <!--     <field name="name">Object 4</field> -->

        <!--   </record> -->

        <!--  -->

    </data>

</odoo>

openacademy/models.py

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

from odoo import models, fields, api

# class openacademy(models.Model):

#     _name = 'openacademy.openacademy'

#     name = fields.Char()

openacademy/security/ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink

access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0

openacademy/templates.xml

<odoo>

<data>

        <!-- <template id="listing"> -->

        <!--   <ul> -->

        <!--     <li t-foreach="objects" t-as="object"> -->

        <!--       <a t-attf-href="{{ root }}/objects/{{ object.id }}"> -->

        <!--         <t t-esc="object.display_name"/> -->

        <!--       </a> -->

        <!--     </li> -->

        <!--   </ul> -->

        <!-- </template> -->

        <!-- <template id="object"> -->

        <!--   <h1><t t-esc="object.display_name"/></h1> -->

        <!--   <dl> -->

        <!--     <t t-foreach="object._fields" t-as="field"> -->

        <!--       <dt><t t-esc="field"/></dt> -->

        <!--       <dd><t t-esc="object[field]"/></dd> -->

        <!--     </t> -->

        <!--   </dl> -->

        <!-- </template> -->

    </data></odoo>

对象关系映射

Odoo的关键组件是ORM层。该层避免 了手工编写大部分SQL,并提供可扩展性和安全性服务2

业务对象被声明为扩展的Python Model,将它们集成到自动持久性系统中。

可以通过在其定义中设置多个属性来配置模型。最重要的属性是 _name必需的,并且在Odoo系统中定义模型的名称。这是模型的最低限度定义:

from odoo import modelsclass

 MinimalModel(models.Model):

    _name = 'test.model'

模型字段

字段用于定义模型可以存储的位置和位置。字段被定义为模型类的属性:

from odoo import models, fields

class LessMinimalModel(models.Model):

    _name = 'test.model2'

 

    name = fields.Char()

共同属性

很像模型本身,它的字段可以通过配置属性作为参数进行配置:

name = field.Char(required=True)

一些属性可用于所有字段,这里是最常见的属性:

stringunicode,默认值:字段的名称)

UI中的字段标签(用户可见)。

requiredbool默认:False

如果True,该字段不能为空,则它必须具有默认值,或者在创建记录时始终被赋予一个值。

helpunicode默认:''

长形式,在用户界面中为用户提供帮助工具提示。

indexbool默认:False

请求Odoo 在列上创建数据库索引

简单字段

有两个广泛的领域:简单字段,它们是直接存储在模型表中的原子值和链接相同模型或不同模型的记录的关系字段。

简单字段的例子是Boolean DateChar

保留字段

Odoo在所有型号中创建了几个字段1。这些字段由系统管理,不应写入。如果有用或必要,可以阅读它们:

idId

其模型中的记录的唯一标识符。

create_dateDatetime

记录的创建日期。

create_uidMany2one

创建记录的用户。

write_dateDatetime

记录的最后修改日期。

write_uidMany2one

用户最后修改记录。

特殊领域

默认情况下,Odoo还需要name所有型号的字段用于各种显示和搜索行为。用于这些目的的字段可以通过设置来覆盖_rec_name

行使

定义一个模型

openacademy模块中定义新的数据模型课程。课程有标题和描述。课程必须有标题。

编辑文件openacademy/models/models.py以包括Course 类。

openacademy/models.py

from odoo import models, fields, api

class Course(models.Model):

_name = 'openacademy.course'

    name = fields.Char(string="Title", required=True)

    description = fields.Text()     

数据文件

Odoo是一个高度数据驱动的系统。虽然行为是使用Python代码定制的 ,但是模块的一部分值在加载时设置的数据中。

小费

一些模块仅存在于向Odoo添加数据

模块数据通过数据文件,带有<record>元素的XML文件 声明。每个<record>元素创建或更新数据库记录。

<odoo>

    <data>

        <record model="{model name}" id="{record identifier}">

            <field name="{a field name}">{a value}</field>

        </record>

</data>

</odoo>

· model Odoo模型的名称,用于记录。

· id是一个外部标识符,它允许引用该记录(而不必知道其数据库内标识符)。

· <field>元素具有name模型中的字段的名称(例如description)。他们的身体是领域的价值。

必须在清单文件中声明数据文件才能加载,它们可以在'data'列表中(始终加载)或'demo'列表中声明(仅在演示模式下加载)。

行使

定义演示数据

创建演示数据填充课程模型与几个示范课程。

编辑文件openacademy/demo/demo.xml以包含一些数据。

openacademy/demo.xml

<odoo>

<data>

        <record model="openacademy.course" id="course0">

            <field name="name">Course 0</field>

            <field name="description">

Course 0's descriptionCan 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>

                                                               

行动和菜单

操作和菜单是数据库中的常规记录,通常通过数据文件声明。可以通过三种方式触发操作:

1. 通过点击菜单项(链接到具体操作)

2. 通过点击视图中的按钮(如果这些连接到动作)

3. 作为对象的上下文动作

因为菜单有些复杂,因为声明有一个<menuitem> 快捷方式来声明一个ir.ui.menu更容易连接到相应的动作。

<record model="ir.actions.act_window" id="action_list_ideas">

    <field name="name">Ideas</field>

    <field name="res_model">idea.idea</field>

<field name="view_mode">tree,form</field>

</record>

<menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"

      action="action_list_ideas"/>


危险

必须在XML文件中的相应菜单之前声明该操作。

数据文件依次执行,操作id必须存在于数据库中,才能创建菜单。

行使

定义新菜单项

定义新的菜单条目以访问OpenAcademy菜单项下的课程。用户应该能够:

· 显示所有课程的列表

· 创建/修改课程

1. 创建openacademy/views/openacademy.xml一个动作和菜单触发动作

2. 将其添加到data列表中openacademy/__manifest__.py

openacademy/__manifest__.py

    'data': [

        # 'security/ir.model.access.csv',

        'templates.xml',        'views/openacademy.xml', 

   ],

    # only loaded in demonstration mode

    'demo': [

openacademy/views/openacademy.xml

<?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>

        <!-- top level menu: no parent -->

        <menuitem id="main_openacademy_menu" name="Open Academy"/>

        <!-- A first level in the left side menu is needed

             before using action= attribute -->

        <menuitem id="openacademy_menu" name="Open Academy"

                  parent="main_openacademy_menu"/>

        <!-- the following menuitem should appear *after*

             its parent openacademy_menu and *after* its

             action course_list_action -->

        <menuitem id="courses_menu" name="Courses" parent="openacademy_menu"

                  action="course_list_action"/>

        <!-- Full id location:

             action="openacademy.course_list_action"

             It is not required when it is the same module -->

    </data></odoo>

基本观点

视图定义了显示模型记录的方式。每种类型的视图都表示可视化模式(记录列表,其聚合图形...)。视图可以通过它们的类型(例如,合作伙伴列表)或者通过其id来特别地请求。对于通用请求,将使用具有正确类型和最低优先级的视图(因此每种类型的最低优先级视图是该类型的默认视图)。

查看继承允许更改其他地方声明的视图(添加或删除内容)。

通用视图声明

视图被声明为模型的记录ir.ui.view。视图类型由arch字段的根元素暗示:

<<record model="ir.ui.view" id="view_id">

    <field name="name">view.name</field>

    <field name="model">object_name</field>

    <field name="priority" eval="16"/>

    <field name="arch" type="xml">

        <!-- view content: <form>, <tree>, <graph>, ... -->

</field>

</record>

危险

视图的内容是XML

因此,arch该字段必须被声明为type="xml"正确解析。

Tree views

Tree views(也称为列表视图)以表格形式显示记录。

它们的根元素是<tree>。树视图的最简单形式只是列出要在表中显示的所有字段(每个字段为列):

<tree string =“Idea list” >

    <field name =“name” />

<field name =“inventor_id” />

 </ tree>

From views

表单用于创建和编辑单个记录。

它们的根元素是<form>。它们由高级结构元素(groups, notebooks)和交互元素(buttons and fields)组成:

<form string="Idea form">

    <group colspan="4">

        <group colspan="2" col="2">

            <separator string="General stuff" colspan="2"/>

            <field name="name"/>

            <field name="inventor_id"/>

        </group>

 

        <group colspan="2" col="2">

            <separator string="Dates" colspan="2"/>

            <field name="active"/>

            <field name="invent_date" readonly="1"/>

        </group>

 

        <notebook colspan="4">

            <page string="Description">

                <field name="description" nolabel="1"/>

            </page>

        </notebook>

 

        <field name="state"/>

</group>

</form>

Search views

搜索视图自定义与列表视图(and other aggregated views)相关联的搜索字段。它们的根元素是<search>,它们由定义可以搜索哪些字段的字段组成:

<search>

    <field name =“name” />

<field name =“inventor_id” />

</ search>

 

 

如果模型没有搜索视图,Odoo会生成一个仅允许在该name字段上进行搜索的视图。

Exercise

Search courses

允许根据他们的标题或描述搜索courses

openacademy/views/openacademy.xml

            </field>

        </record>

        <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"/>

                </search>

            </field>

        </record>

        <!-- window action -->

        <!--            The following tag is an action definition for a "window action",

模型之间的关系

来自模型的记录可能与另一个模型的记录有关。例如,销售订单记录与包含客户端数据的客户端记录有关它也与其销售订单行记录有关。

行使

创建会话模型

对于Open Academy模块,我们考虑一个会话模型:一个会话是给定受众在给定时间教授的课程。

创建会话模型。会话的名称,开始日期,持续时间和座位数。添加动作和菜单项以显示它们。通过菜单项使新模型可见。

1. 创建一流的会议openacademy/models/models.py

2. 添加对会话对象的访问openacademy/view/openacademy.xml

openacademy/models.py

 

name = fields.Char(string="Title", required=True)

    description = fields.Text()

class Session(models.Model):

    _name = 'openacademy.session'

    name = fields.Char(required=True)

    start_date = fields.Date()

    duration = fields.Float(digits=(6, 2), help="Duration in days")

    seats = fields.Integer(string="Number of seats")

openacademy/views/openacademy.xml

        <!-- Full id location:

             action="openacademy.course_list_action"

             It is not required when it is the same module -->

        <!-- 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>

        <record model="ir.actions.act_window" id="session_list_action">

            <field name="name">Sessions</field>

            <field name="res_model">openacademy.session</field>

            <field name="view_type">form</field>

            <field name="view_mode">tree,form</field>

        </record>

        <menuitem id="session_menu" name="Sessions"

                  parent="openacademy_menu"

                  action="session_list_action"/>

</data>

</odoo>

注意

digits=(6, 2)指定浮点数的精度:6是总位数,而2是逗号后的位数。请注意,它会导致逗号之前的数字最多为4

关系领域

关系字段链接相同模型(层次结构)或不同模型之间的记录。

关系字段类型有:

Many2one(other_model, ondelete='set null')

与其他对象的简单链接:

print foo.other_id.name

 

One2many(other_model, related_field)

虚拟关系,一个倒数Many2oneOne2many作为记录的容器,访问它会导致一个(可能是空的)记录集:

for other in foo.other_ids:

    print other.name

危险

因为One2many是一个虚拟关系,所以必须有一个Many2one字段 other_model,其名称必须related_field

Many2many(other_model)

双向多重关系,一方的任何记录可以与另一方的任何数量的记录相关。作为记录的容器,访问它也会导致一组可能为空的记录集:

for other in foo.other_ids:

    print other.name

Exercise

很多人关系

使用many2one,修改课程会话模型以反映与其他模型的关系:

· 课程有一个负责任的用户该字段的值是内置模型的记录res.users

· 会话有一个教练 ; 该字段的值是内置模型的记录res.partner

· 一个课程与课程有关 ; 该字段的值是模型的记录,openacademy.course是必需的。

· 适应观点。

1. 将相关Many2one字段添加到模型中

2. 将它们添加到视图中。

openacademy/models.py

    name = fields.Char(string="Title", required=True)

    description = fields.Text()

responsible_id = fields.Many2one('res.users',

        ondelete='set null', string="Responsible", index=True)

class Session(models.Model):

    _name = 'openacademy.session'

    start_date = fields.Date()

    duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

    instructor_id = fields.Many2one('res.partner', string="Instructor")

    course_id = fields.Many2one('openacademy.course',

        ondelete='cascade', string="Course", required=True)

openacademy/views/openacademy.xml

                    <sheet>

                        <group>

                            <field name="name"/>

                            <field name="responsible_id"/>

                        </group>

                        <notebook>

                            <page string="Description">

            </field>

        </record>

        <!-- override the automatically generated list view for courses -->

        <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="responsible_id"/>

                </tree>

            </field>

        </record>

        <!-- window action -->

        <!--            The following tag is an action definition for a "window action",

                <form string="Session Form">

                    <sheet>

                        <group>

                            <group string="General">

                                <field name="course_id"/>

                                <field name="name"/>

                                <field name="instructor_id"/>

                            </group>

                            <group string="Schedule">

                                <field name="start_date"/>

                                <field name="duration"/>

                                <field name="seats"/>

                            </group>

                        </group>

                    </sheet>

                </form>

            </field>

        </record>

        <!-- session tree/list view -->

        <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">

                    <field name="name"/>

                    <field name="course_id"/>

                </tree>

            </field>

        </record>

        <record model="ir.actions.act_window" id="session_list_action">

            <field name="name">Sessions</field>

            <field name="res_model">openacademy.session</field>

Exercise

反向一个关系

使用反向关系字段one2many,修改模型以反映课程和会话之间的关系。

1. 修改Course类,和

2. 在课程表单视图中添加该字段。

openacademy/models.py

    instructor_id = fields.Many2one('res.partner', string="Instructor")

    course_id = fields.Many2one('openacademy.course',

        ondelete='cascade', string="Course", required=True)    attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml

                                <field name="seats"/>

                            </group>

                        </group>                        <label for="attendee_ids"/>                        <field name="attendee_ids"/>                    </sheet>

                </form>

            </field>

行使

多重多关系

使用关系字段many2many,修改会话模型以将每个会话关联到一组与会者。参加者将由合作伙伴记录代表,因此我们将与内置模型相关res.partner。相应地调整意见。

1. 修改Session类,和

2. 在窗体视图中添加该字段。

openacademy / models.py

    instructor_id = fieldsMany2one ('res.partner' string= “Instructor”

    course_id = fieldsmany2one ('openacademy.course'

        ondelete= 'cascade' string= “Course” required= True )

attendee_ids= fieldsMany2many ('res.partner'string= “Attendees”       

openacademy /视图/ openacademy.xml

                                <field name =“seats” />

                            </ group>

                        </ group>

<labelfor = “attendee_ids” />

 <fieldname = “attendee_ids” />

 </ sheet>

    </ form>

      </ field>                                                                      

                

            

遗产

模型继承

Odoo提供了两种继承机制,以模块化方式扩展现有的模型。

第一个继承机制允许模块修改在另一个模块中定义的模型的行为:

· 向模型添加字段,

· 覆盖模型上字段的定义,

· 对模型添加约束,

· 向模型添加方法,

· 覆盖模型上的现有方法。

第二个继承机制(委托)允许将模型的每个记录链接到父模型中的记录,并提供对父记录的字段的透明访问。

 

 

也可以看看

· _inherit

· _inherits

查看继承

Odoo不需要修改现有的视图(通过覆盖它们),而是在根视图之上应用子扩展视图的视图继承,并可以从父级添加或删除内容。

扩展视图使用inherit_id字段引用其父级,而不是单个视图,其arch字段由任意数量的xpath元素组成, 选择和更改其父视图的内容:

<!-- improved idea categories list -->

<record id="idea_category_list2" model="ir.ui.view">

    <field name="name">id.category.list2</field>

    <field name="model">idea.category</field>

    <field name="inherit_id" ref="id_category_list"/>

    <field name="arch" type="xml">

        <!-- find field description and add the field             idea_ids after it -->

        <xpath expr="//field[@name='description']" position="after">

          <field name="idea_ids" string="Number of ideas"/>

        </xpath>

</field>

</record>

expr

一个XPath表达式中选择父视图的单个元素。如果不匹配任何元素或多于一个元素,则会引发错误

position

适用于匹配元素的操作:

inside

xpath在匹配的元素的末尾附加身体

replace

xpath“body” 替换匹配的元素,用$0原始元素替换新主体中的任何节点

before

xpath在匹配的元素之前插入身体作为兄弟姐妹

after

xpaths在匹配的元素之后插入身体作为兄弟姐妹

attributes

改变使用特殊的匹配元素的属性 attribute的元素的xpath的身体

小费

当匹配单个元素时,position可以在要查找的元素上直接设置该属性。以下两个遗产都会得到相同的结果。

<xpath expr="//field[@name='description']" position="after">

<field name="idea_ids" />

</xpath>

<field name="description" position="after">

<field name="idea_ids" />

</field>

行使

更改现有内容

· 使用模型继承,修改现有的合作伙伴模型以添加 instructor布尔字段,以及对应于会话伙伴关系的many2many字段

· 使用视图继承,在合作伙伴表单视图中显示此字段

注意

这是介绍开发人员模式检查视图,找到其外部ID和放置新字段的位置的机会。

1. 创建一个文件openacademy/models/partner.py并将其导入 __init__.py

2. 创建一个文件openacademy/views/partner.xml并将其添加到 __manifest__.py

openacademy/__init__.py

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

from . import controllersfrom . import models

from . import partner

openacademy/__manifest__.py

        # 'security/ir.model.access.csv',

        'templates.xml',

        'views/openacademy.xml',

        'views/partner.xml',

    ],

    # only loaded in demonstration mode

    'demo': [

openacademy/partner.py

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

from odoo import fields, models

class Partner(models.Model):

_inherit = 'res.partner'    

# Add a new column to the res.partner model, by default partners are not    

# instructors

    instructor = fields.Boolean("Instructor", default=False)

    session_ids = fields.Many2many('openacademy.session',

        string="Attended Sessions", readonly=True)

openacademy/views/partner.xml

<?xml version="1.0" encoding="UTF-8"?>

 <odoo>

<data>

        <!-- Add instructor field to existing view -->

        <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"/>

    </data>

</odoo>

Odoo中,是编码记录条件的值。域是用于选择模型记录子集的标准列表。每个标准是一个带有字段名称,运算符和值的三元组。

例如,当在产品型号上使用时,以下域选择单价超过1000所有服务

[('product_type' '=' 'service'), 'unit_price' '>' 1000]

默认情况下,条件与隐式AND组合。逻辑运算符 &AND),|OR)和!NOT)可用于明确组合条件。它们用于前缀位置(操作符在其参数之前插入,而不是之间)。例如选择产品这是服务有单位价格是不是 1000 - 2000年间

['|'

    'product_type' '=' 'service'),

    '!' '&'

        'unit_price' '> =' 1000),

        'unit_price' '<' 2000]

domain当尝试在客户端界面中选择记录时,可以将一个参数添加到关系字段中以限制关系的有效记录。

行使

关系领域

当选择会话的教师时,只有教师(具有instructor设置的合作伙伴True)应该是可见的。

openacademy/models.py

    duration = fields.Float(digits=(6, 2), help="Duration in days")

    seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner', string="Instructor",

        domain=[('instructor', '=', True)])

    course_id = fields.Many2one('openacademy.course',

        ondelete='cascade', string="Course", required=True)

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

注意

声明为文字列表的域被评估为服务器端,并且不能引用右侧的动态值,声明为字符串的域被评估为客户端,并允许右侧的字段名称

行使

更复杂的域名

创建新的合作伙伴类别教师/ 1教师/ 2。会话的教练可以是教练或教师(任何级别)。

1. 修改会话模型的域

2. 修改openacademy/view/partner.xml以访问 合作伙伴类别

openacademy/models.py

    seats = fields.Integer(string="Number of seats")

 

instructor_id = fields.Many2one('res.partner', string="Instructor",

        domain=['|', ('instructor', '=', True),

                     ('category_id.name', 'ilike', "Teacher")])

    course_id = fields.Many2one('openacademy.course',

        ondelete='cascade', string="Course", required=True)

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/partner.xml

        <menuitem id="contact_menu" name="Contacts"

                  parent="configuration_menu"

                  action="contact_list_action"/>

        <record model="ir.actions.act_window" id="contact_cat_list_action">

            <field name="name">Contact Tags</field>

            <field name="res_model">res.partner.category</field>

            <field name="view_mode">tree,form</field>

        </record>

        <menuitem id="contact_cat_menu" name="Contact Tags"

                  parent="configuration_menu"

                  action="contact_cat_list_action"/>

        <record model="res.partner.category" id="teacher1">

            <field name="name">Teacher / Level 1</field>

        </record>

        <record model="res.partner.category" id="teacher2">

            <field name="name">Teacher / Level 2</field>

        </record>

</data></odoo>

计算字段和默认值

到目前为止,字段已直接存储在数据库中并直接从数据库中检索。也可以计算字段。在这种情况下,字段的值不会从数据库中检索,而是通过调用模型的方法在运行中计算。

要创建计算字段,请创建一个字段并将其属性设置 compute为方法的名称。计算方法应该简单地设置字段的值来计算每个记录 self

危险

self 是一个集合

对象self是一个记录集,即一个有序的记录集合。它支持标准的Python操作的集合,像 len(self)iter(self),加上额外的一套操作,比如recs1 + recs2

迭代过来self给出记录一个一个,其中每个记录本身是大小为1的集合。您可以使用点符号来访问/分配单个记录上的字段,如record.name

import randomfrom odoo import models, fields, api

class ComputedModel(models.Model):

    _name = 'test.computed'

 

    name = fields.Char(compute='_compute_name')

 

    @api.multi

    def _compute_name(self):

        for record in self:

            record.name = str(random.randint(1, 1e6))

 

依赖

计算字段的值通常取决于计算记录上其他字段的值。ORM希望开发人员使用装饰器来指定计算方法的依赖关系depends()ORM使用给定的依赖关系来触发字段的重新计算,只要某些依赖关系被修改:

from odoo import models, fields, api

class ComputedModel(models.Model):

    _name = 'test.computed'

 

    name = fields.Char(compute='_compute_name')

    value = fields.Integer()

 

    @api.depends('value')

    def _compute_name(self):

        for record in self:

            record.name = "Record with value %s" % record.value

行使

计算字段

· 将占席席位的百分比添加到会议模型中

· 在树中显示该字段并形成视图

· 将该字段显示为进度条

1. 将计算字段添加到会话

2. 会话视图中显示该字段:

openacademy/models.py

    course_id = fields.Many2one('openacademy.course',

        ondelete='cascade', string="Course", required=True)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

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

    @api.depends('seats', 'attendee_ids')

    def _taken_seats(self):

        for r in self:

            if not r.seats:

                r.taken_seats = 0.0

            else:

                r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats

openacademy/views/openacademy.xml

                                <field name="start_date"/>

                                <field name="duration"/>

                                <field name="seats"/>

                                <field name="taken_seats" widget="progressbar"/>                                </group>

                        </group>

                        <label for="attendee_ids"/>

                <tree string="Session Tree">

                    <field name="name"/>

                    <field name="course_id"/>

                    <field name="taken_seats" widget="progressbar"/>

                </tree>

            </field>

        </record>

默认值

任何字段都可以被赋予默认值。在字段定义中,添加选项 default=X,其中XPython文字值(booleanintegerfloatstring),或者使用记录集并返回值的函数:

name = fields.Char(default="Unknown")user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

注意

该对象self.env允许访问请求参数和其他有用的东西:

· self.env.cr或者self._cr是数据库游标对象它用于查询数据库

· self.env.uid或者self._uid是当前用户的数据库ID

· self.env.user 是当前用户的记录

· self.env.context或者self._context是上下文字典

· self.env.ref(xml_id) 返回对应于XML标识的记录

· self.env[model_name] 返回给定模型的实例

行使

活动对象 -默认值

· start_date默认值定义为今天(见 Date)。

· active在类Session中添加一个字段,默认情况下将会话设置为活动状态。

openacademy / models.py

   _name = 'openacademy.session'

 

name = fields.Char(required=True)

    start_date = fields.Date(default=fields.Date.today)

    duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

    active = fields.Boolean(default=True)

    instructor_id = fields.Many2one('res.partner', string="Instructor",

        domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml

                                <field name="course_id"/>

                                <field name="name"/>

                                <field name="instructor_id"/>

                                <field name="active"/>

                            </group>

                            <group string="Schedule">

                                <field name="start_date"/>

注意

Odoo内置规则制作字段active设置为False不可见。

平变化

“onchange”机制为客户端界面提供了一种方式,只要用户填写一个字段中的值,就不用保存任何数据就可以更新表单。

例如,假设模型有三个字段amountunit_price并且 price当您修改任何其他字段时,您希望更新表单上的价格。为了实现这一点,定义一个方法,它self表示窗体视图中的记录,并用它onchange() 来对其进行装饰,以指定哪个字段必须被触发。你所做的任何改变 self都会反映在表格上。

<!-- content of form view -->

<field name="amount"/>

<field name="unit_price"/>

<field name="price" readonly="1"/>

# onchange handler@api.onchange('amount', 'unit_price')

def _onchange_price(self):

    # set auto-changing field

    self.price = self.amount * self.unit_price

    # Can optionally return a warning and domains

    return {

        'warning': {

            'title': "Something bad happened",

            'message': "It was very bad indeed",

        }

    }

对于计算的字段,onchange内置有价值的行为,可以通过使用会话表单来看到:更改席位或参与者的数量,并且taken_seats进度条将自动更新。

行使

警告

添加一个明确的onchange来警告有关无效值,比如负数的席位,或比座席多的参与者。

openacademy / models.py

                r.taken_seats = 0.0

            else:

                r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats

@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",

                },

            }

  

模型约束

Odoo提供两种方式来设置自动验证的不变量: Python constraints SQL constraints

Python约束被定义为用constrains()记录集装饰并在记录集上调用的方法 。装饰器指定约束中涉及哪些字段,以便在其中一个被修改时自动计算约束。如果该方法的不变量不满足,则该方法将会引发异常:

from odoo.exceptions import ValidationError

@api.constrains('age')def _check_something(self):

    for record in self:

        if record.age > 20:

            raise ValidationError("Your record is too old: %s" % record.age)

    # all records passed the test, don't return anything

行使

添加Python约束

添加一个约束,该约束检查教师不在他/她自己的会话的与会者中。

openacademy/models.py

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

from odoo import models, fields, api, exceptions

class Course(models.Model):

    _name = 'openacademy.course'

                    'message': "Increase seats or remove excess attendees",

                },

            }

@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")   

通过model属性定义SQL约束 _sql_constraints。后者被分配给一个三元组的列表(name, sql_definition, message),其中name有一个有效的SQL约束名称,sql_definition是一个table_constraint表达式,并且message是错误消息。

行使

添加SQL约束

PostgreSQL文档的帮助下,添加以下约束:

1. 检查课程描述和课程名称是不同的

2. 使课程名称独一无二

openacademy / models.py

openacademy/models.py

    session_ids = fields.One2many(

        'openacademy.session', 'course_id', string="Sessions")

_sql_constraints = [

        ('name_description_check',

         'CHECK(name != description)',

         "The title of the course should not be the description"),

        ('name_unique',

         'UNIQUE(name)',

         "The course title must be unique"),

    ]class Session(models.Model):

    _name = 'openacademy.session'

行使

练习6 -添加重复选项

由于我们为课程名称唯一性添加了约束,所以不可能再使用重复函数(Form‣Duplicate)。

重新实现自己的复制方法,允许复制课程对象,将原始名称更改为原始名称的副本

openacademy/models.py

    session_ids = fields.One2many(

        'openacademy.session', 'course_id', string="Sessions")

@api.multi

    def copy(self, default=None):

        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)

    _sql_constraints = [

        ('name_description_check',

         'CHECK(name != description)',

高级视图

树的意见

树视图可以采取补充属性来进一步自定义他们的行为:

decoration-{$name}

允许根据相应记录的属性更改行的文本的样式。

值是Python表达式。对于每个记录,表达式将使用记录的属性作为上下文值进行计算,如果true相应的样式应用于该行。其他上下文值是 uid(当前用户的id)和current_date(当前日期作为表单的字符串yyyy-MM-dd)。

{$name}可以是bffont-weight: bold), it ),font-style: italic或任何自举上下文颜色dangerinfomutedprimarysuccesswarning)。

<tree string =“Idea Categories”  decoration-info =“state =='draft'”

    decoration-danger =“state =='trashed'” >

    <field name =“name” />

<field name =“state” / >

</ tree>

editable

无论是"top""bottom"。使树视图可以原位编辑(而不必通过窗体视图),该值是新行显示的位置。

行使

列表着色

修改会话树视图,使持续时间少于5天的会话为蓝色,持续超过15天的会话为红色。

修改会话树视图:

openacademy /视图/ openacademy.xml

            <field name="name">session.tree</field>

            <field name="model">openacademy.session</field>

            <field name="arch" type="xml">

                <tree string="Session Tree" decoration-info="duration<5"

 decoration-danger="duration>15">

                    <field name="name"/>

                    <field name="course_id"/>

                    <field name="duration" invisible="1"/>

                    <field name="taken_seats" widget="progressbar"/>

                </tree>

            </field>

日历

将记录显示为日历事件。它们的根元素是<calendar>它们的最常见的属性是:

color

用于颜色分割的字段的名称。颜色会自动分配到事件中,但同一颜色段中的事件(对于其@color字段具有相同值的记录)将被赋予相同的颜色。

date_start

记录的字段保存事件的开始日期/时间

date_stop (可选的)

记录的字段保存事件的结束日期/时间

字段(定义每个日历事件的标签)

<calendar string="Ideas" date_start="invent_date" color="inventor_id">

    <field name="name"/></calendar>

行使

日历视图

将日历视图添加到会话模型,使用户能够查看与开放学院相关联的事件。

1.添加end_datestart_date 计算的字段duration

逆函数使字段可写,并允许在日历视图中移动会话(通过拖放)

1. 将日历视图添加到会话模型

2. 并将日历视图添加到会话模型的操作

openacademy/models.py

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

from datetime import timedelta

from odoo import models, fields, api, exceptions

class Course(models.Model):

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

 

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

    end_date = fields.Date(string="End Date", store=True,

        compute='_get_end_date', inverse='_set_end_date')

    @api.depends('seats', 'attendee_ids')

    def _taken_seats(self):

                },

            }

@api.depends('start_date', 'duration')

    def _get_end_date(self):

        for r in self:

            if not (r.start_date and r.duration):

                r.end_date = r.start_date

                Continue

            # Add duration to start_date, but: Monday + 5 days = Saturday, so

            # subtract one second to get on Friday instead

            start = fields.Datetime.from_string(r.start_date)

            duration = timedelta(days=r.duration, seconds=-1)

            r.end_date = start + duration

    def _set_end_date(self):

        for r in self:

            if not (r.start_date and r.end_date):

                Continue

            # Compute the difference between dates, but: Friday - Monday = 4 days,

            # so add one day to get 5 days instead

            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 + 1

    @api.constrains('instructor_id', 'attendee_ids')

    def _check_instructor_not_in_attendees(self):

        for r in self:

openacademy/views/openacademy.xml

            </field>

        </record>

        <!-- calendar view -->

        <record model="ir.ui.view" id="session_calendar_view">

            <field name="name">session.calendar</field>

            <field name="model">openacademy.session</field>

            <field name="arch" type="xml">

                <calendar string="Session Calendar" date_start="start_date"

                          date_stop="end_date"

                          color="instructor_id">

                    <field name="name"/>

                </calendar>

            </field>

        </record>

        <record model="ir.actions.act_window" id="session_list_action">

            <field name="name">Sessions</field>

            <field name="res_model">openacademy.session</field>

            <field name="view_type">form</field>

            <field name="view_mode">tree,form,calendar</field>

        </record>

 

        <menuitem id="session_menu" name="Sessions"

搜索视图

搜索视图<field>元素可以@filter_domain覆盖在给定字段上搜索生成的域。在给定的域中, self表示用户输入的值。在下面的例子中,它是用来在两个字段进行搜索namedescription

搜索视图还可以包含<filter>用于预定义搜索的切换的元素。过滤器必须具有以下属性之一:

domain

将给定的域添加到当前搜索

context

为当前搜索添加一些上下文; 使用密钥group_by对给定字段名称的结果进行分组

<search string="Ideas">

    <field name="name"/>

    <field name="description" string="Name and description"

           filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>

    <field name="inventor_id"/>

    <field name="country_id" widget="selection"/>

 

    <filter name="my_ideas" string="My Ideas"

            domain="[('inventor_id', '=', uid)]"/>

    <group string="Group By">

        <filter name="group_by_inventor" string="Inventor"

                context="{'group_by': 'inventor_id'}"/>

</group>

</search>

要在操作中使用非默认搜索视图,应使用search_view_id操作记录的字段进行链接 

该操作还可以通过其context字段设置搜索字段的默认值 :窗体的上下文键 将使用提供的值初始化field_name。搜索过滤器必须具有可选功能以具有默认值,并且表现为布尔值(默认情况下只能启用)。search_default_field_name@name

行使

搜索视图

1. 添加一个按钮来过滤当前用户在课程搜索视图中负责的课程。默认选择它。

2. 添加按钮,由负责的用户对课程进行分组。

openacademy/views/openacademy.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>

            <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>

cantt

警告

Cantt视图需要企业版本中存在的web_gantt模块 

水平条形图通常用于显示项目规划和进步,它们的根本要素是<gantt>

<gantt string="Ideas"

       date_start="invent_date"

       date_stop="date_finished"

       progress="progress"

       default_group_by="inventor_id" />

行使

甘特图

添加甘特图,使用户能够查看与Open Academy模块链接的会话调度。会议应由教师分组。

1. 创建表示会话持续时间(以小时为单位)的计算字段

2. 添加甘特图视图的定义,并将甘特图视图添加到 会话模型的操作

openacademy/models.py

    end_date = fields.Date(string="End Date", store=True,

        compute='_get_end_date', inverse='_set_end_date')

hours = fields.Float(string="Duration in hours",

                         compute='_get_hours', inverse='_set_hours')

    @api.depends('seats', 'attendee_ids')

    def _taken_seats(self):

        for r in self:

            end_date = fields.Datetime.from_string(r.end_date)

            r.duration = (end_date - start_date).days + 1

@api.depends('duration')

    def _get_hours(self):

        for r in self:

            r.hours = r.duration * 24

    def _set_hours(self):

        for r in self:

            r.duration = r.hours / 24

    @api.constrains('instructor_id', 'attendee_ids')

    def _check_instructor_not_in_attendees(self):

        for r in self:

openacademy/views/openacademy.xml

            </field>

        </record>

        <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.actions.act_window" id="session_list_action">

            <field name="name">Sessions</field>

            <field name="res_model">openacademy.session</field>

            <field name="view_type">form</field>

            <field name="view_mode">tree,form,calendar,gantt</field>

        </record>

 

        <menuitem id="session_menu" name="Sessions"

图形视图

图形视图允许对模型进行聚合概述和分析,它们的根元素是<graph>

注意

数据透视图(元素<pivot>)是一个多维表格,允许选择文件管理器和维度,以获得正确的聚合数据集,然后再转到更加图形化的概述。枢轴视图与图形视图共享相同的内容定义。

图形视图具有4种显示模式,使用该@type属性选择默认模式 

栏(默认)

条形图,第一个维度用于在横轴上定义组,其他维度定义每个组中的聚合条。

默认情况下条是并排的一面,他们可以通过堆叠 @stacked="True"<graph>

线

二维线图

馅饼

二维派

图形视图包含<field>带有必需@type属性的值:

row (默认)

该字段应该默认合并

measure

该字段应该聚合而不是分组

<<graph string="Total idea score by Inventor">

    <field name="inventor_id"/>

<field name="score" type="measure"/>

</graph>

警告

图表视图对数据库值执行聚合,但它们不适用于未存储的计算字段。

行使

图形视图

在会话对象中添加一个图形视图,每个课程显示条形图形式的与会者人数。

1. 将出席人数作为存储的计算字段添加

2. 然后添加相关视图

openacademy/models.py

    hours = fields.Float(string="Duration in hours",

                         compute='_get_hours', inverse='_set_hours')

attendees_count = fields.Integer(

        string="Attendees count", compute='_get_attendees_count', store=True)

    @api.depends('seats', 'attendee_ids')

    def _taken_seats(self):

        for r in self:

        for r in self:

            r.duration = r.hours / 24

@api.depends('attendee_ids')

    def _get_attendees_count(self):

        for r in self:

            r.attendees_count = len(r.attendee_ids)

    @api.constrains('instructor_id', 'attendee_ids')

    def _check_instructor_not_in_attendees(self):

        for r in self:

openacademy/views/openacademy.xml

            </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>

        <record model="ir.actions.act_window" id="session_list_action">

            <field name="name">Sessions</field>

            <field name="res_model">openacademy.session</field>

            <field name="view_type">form</field>

            <field name="view_mode">tree,form,calendar,gantt,graph</field>

        </record>

 

        <menuitem id="session_menu" name="Sessions"

看板

用于组织任务,生产过程等...其根元素是 <kanban>

看板视图显示了可能按列分组的一组卡。每个卡表示一个记录,每个列表示聚合字段的值。

例如,项目任务可以按阶段组织(每列是一个阶段),或者由负责人(每列是用户)等组织。

看板视图将每张卡片的结构定义为表单元素(包括基本HTML)和QWeb的组合

行使

看板视图

添加一个看板视图,显示按课程分组的会话(列因此是课程)。

1. color会话模型添加一个整数字段

2. 添加看板视图并更新操作

openacademy/models.py

    duration = fields.Float(digits=(6, 2), help="Duration in days")

    seats = fields.Integer(string="Number of seats")

active = fields.Boolean(default=True)

    color = fields.Integer()

    instructor_id = fields.Many2one('res.partner', string="Instructor",

        domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml

 </record>

       <recordmodel="ir.ui.view"id="view_openacad_session_kanban">
           <fieldname="name">openacad.session.kanban</field>
           <fieldname="model">openacademy.session</field>
           <fieldname="arch"type="xml">
               <kanbandefault_group_by="course_id">
                   <fieldname="color"/>
                   <templates>
                       <tt-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' : ''}}"
>
                               <divclass="oe_dropdown_kanban">
                                   <!-- dropdown menu -->
                                   
<divclass="oe_dropdown_toggle">
                                       <iclass="fa fa-bars fa-lg"/>
                                       <ulclass="oe_dropdown_menu">
                                           <li>
                                               <atype="delete">Delete</a>
                                           </li>
                                           <li>
                                               <ulclass="oe_kanban_colorpicker"
                                                   data-field="color"
/>
                                           </li>
                                       </ul>
                                   </div>
                                   <divclass="oe_clear"></div>
                               </div>
                               <divt-attf-class="oe_kanban_content">
                                   <!-- title -->
                                   
Session name:
                                   <fieldname="name"/>
                                   <br/>
                                   Start date:
                                   <fieldname="start_date"/>
                                   <br/>
                                   duration:
                                   <fieldname="duration"/>
                               </div>
                           </div>
                       </t>
                   </templates>
               </kanban>
           </field>
       </record>

       <recordmodel="ir.actions.act_window"id="session_list_action">
           <fieldname="name">Sessions</field>
           <fieldname="res_model">openacademy.session</field>
           <fieldname="view_type">form</field>
           <fieldname="view_mode">tree,form,calendar,gantt,graph,kanban</field>
       </record>

       <menuitemid="session_menu"name="Sessions"
                 parent="openacademy_menu"

工作流程

工作流程是与描述其动态的业务对象相关联的模型。工作流也用于跟踪随时间推移的进程。

行使

几乎是一个工作流程

state会话模型添加一个字段。它将被用来定义一个工作流。

可以有三种可能的状态:草案(默认),已确认和完成。

在会话表单中,添加(只读)字段可视化状态,按钮更改它。有效的转换是:

· 草稿 - >确认

· 已确认 - >草稿

· 已确认 - >完成

· 完成 - >草稿

1. 添加一个新的state字段

2. 添加状态转换方法,可以从视图按钮调用这些方法来更改记录的状态

3. 并将相关按钮添加到会话的窗体视图中

openacademy / models.py

 attendees_count = fields.Integer(
    string="Attendees count", compute='_get_attendees_count', store=True)

state = fields.Selection([
    ('draft', "Draft"),
    ('confirmed', "Confirmed"),
    ('done', "Done"),
], default='draft')

@api.multi
def action_draft(self):
    self.state = 'draft'

@api.multi
def action_confirm(self):
    self.state = 'confirmed'

@api.multi
def action_done(self):
    self.state = 'done'

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
    for r in self:

openacademy /视图/ openacademy.xml

            <field name="model">openacademy.session</field>

            <field name="arch" type="xml">

                <form string="Session Form">

                    <header>

                        <button name="action_draft" type="object"

                                string="Reset to draft"

                                states="confirmed,done"/>

                        <button name="action_confirm" type="object"

                                string="Confirm" states="draft"

                                class="oe_highlight"/>

                        <button name="action_done" type="object"

                                string="Mark as done" states="confirmed"

                                class="oe_highlight"/>

                        <field name="state" widget="statusbar"/>

                    </header>

                    <sheet>

                        <group>

                            <group string="General">

                     

工作流程可能与Odoo中的任何对象相关联,并且可以完全自定义。工作流用于构建和管理业务对象和文档的生命周期,并使用图形工具定义转换,触发器等。工作流,活动(节点或动作)和转换(条件)都像往常一样被声明为XML记录。在工作流中导航的令牌称为工作项。

警告

与模型相关联的工作流仅在创建模型记录时创建。因此,在工作流定义之前没有与会话实例关联的工作流实例

行使

工作流程

通过真实的工作流替换特设会话工作流程。转换 会话表单视图,使其按钮调用工作流而不是模型的方法。

openacademy/__manifest__.py

        'templates.xml',

        'views/openacademy.xml',

        'views/partner.xml',

        'views/session_workflow.xml',

    ],

    # only loaded in demonstration mode

    'demo': [

openacademy/models.py

        ('draft', "Draft"),

        ('confirmed', "Confirmed"),

        ('done', "Done"),

    ])

    @api.multi

    def action_draft(self):

openacademy/views/openacademy.xml

            <field name="arch" type="xml">

                <form string="Session Form">

                    <header>

                        <button name="draft" type="workflow"

                                string="Reset to draft"

                                states="confirmed,done"/>

                        <button name="confirm" type="workflow"

                                string="Confirm" states="draft"

                                class="oe_highlight"/>

                        <button name="done" type="workflow"

                                string="Mark as done" states="confirmed"

                                class="oe_highlight"/>

                        <field name="state" widget="statusbar"/>

openacademy/views/session_workflow.xml

<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>

        <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">function</field>

            <field name="action">action_draft()</field>

        </record>

        <record model="workflow.activity" id="confirmed">

            <field name="name">Confirmed</field>

            <field name="wkf_id" ref="wkf_session"/>

            <field name="kind">function</field>

            <field name="action">action_confirm()</field>

        </record>

        <record model="workflow.activity" id="done">

            <field name="name">Done</field>

            <field name="wkf_id" ref="wkf_session"/>

            <field name="kind">function</field>

            <field name="action">action_done()</field>

        </record>

        <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>

    </data></odoo>

小费

为了检查工作流的实例是否正确地与会话一起创建,请转到设置技术工作流程实例

行使

自动转换

当会议席位超过一半时,会自动将会话从“ 草稿转为“ 已确认

openacademy/views/session_workflow.xml

           <field name="act_to" ref="done"/>

            <field name="signal">done</field>

        </record>

        <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完全创建。

openacademy/views/session_workflow.xml

            <field name="on_create">True</field>

        </record>

        <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>

        <record model="ir.actions.server" id="set_session_to_confirmed">

            <field name="name">Set session to Confirmed</field>

            <field name="model_id" ref="model_openacademy_session"/>

            <field name="code">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>

        <record model="ir.actions.server" id="set_session_to_done">

            <field name="name">Set session to Done</field>

            <field name="model_id" ref="model_openacademy_session"/>

            <field name="code">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>

 

        <record model="workflow.transition" id="session_draft_to_confirmed">

安全

访问控制机制必须配置为实现一致的安全策略。

基于组的访问控制机制

组作为模型上的常规记录创建res.groups,并通过菜单定义授予菜单访问权限。但即使没有菜单,对象仍然可以间接访问,因此必须为组定义实际的对象级权限(读取,写入,创建,取消链接)。它们通常通过模块中的CSV文件插入。也可以使用字段的groups属性来限制对视图或对象的特定字段的访问。

访问权

访问权限定义为模型的记录ir.model.access。每个访问权限与模型,组(或不用于全局访问的组)相关联,以及一组权限:读取,写入,创建,取消链接。这种访问权限通常由以其模型命名的CSV文件创建: ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink

access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0

access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0

行使

通过Odoo界面添加访问控制

创建一个新用户“John Smith”。然后创建一个“OpenAcademy / Session Read”组,对Session模型进行读访问。

1. 通过设置创建新用户John Smith通过 用户用户

2. session_read通过 设置创建新组用户,它应该具有会话模型的读访问权限

3. 编辑约翰·史密斯,使他们成为session_read

4. 约翰·史密斯身份登录,以检查访问权限是否正确

行使

通过模块中的数据文件添加访问控制

使用数据文件,

· 创建一个OpenAcademy / Manager组,可以完全访问所有OpenAcademy模型

· 会话课程由所有用户可读

1. 创建一个新文件openacademy/security/security.xml来保存OpenAcademy Manager

2. openacademy/security/ir.model.access.csv使用模型的访问权限编辑文件

3. 最后更新openacademy/__manifest__.py以添加新的数据文件

openacademy/__manifest__.py

 

# always loaded

    'data': [

        'security/security.xml',

        'security/ir.model.access.csv',

        'templates.xml',

        'views/openacademy.xml',

        'views/partner.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,0session_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>

/ data> </ odoo>                                       

记录规则

记录规则限制对给定模型的记录子集的访问权限。规则是模型的记录ir.rule,并且与模型,多个组(许多字段),适用该限制的权限以及域相关联。域指定访问权限受限于哪些记录。

以下是防止未处于状态的引线的删除的规则示例cancel。请注意,该字段的值groups必须遵循与write()ORM 方法相同的约定。

<record id="delete_cancelled_only" model="ir.rule">

    <field name="name">Only cancelled leads may be deleted</field>

    <field name="model_id" ref="crm.model_crm_lead"/>

    <field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>

    <field name="perm_read" eval="0"/>

    <field name="perm_write" eval="0"/>

    <field name="perm_create" eval="0"/>

    <field name="perm_unlink" eval="1" />

<field name="domain_force">[('state','=','cancel')]</field>

</record>

行使

记录规则

为模型课程和“OpenAcademy / Manager”组添加记录规则,限制writeunlink访问课程负责人。如果课程没有责任,组中的所有用户都必须能够修改它。

创建新规则openacademy/security/security.xml

openacademy/security/security.xml

        <record id="group_manager" model="res.groups">

            <field name="name">OpenAcademy / Manager</field>

        </record>

            <record id="only_responsible_can_modify" model="ir.rule">

            <field name="name">Only Responsible can modify Course</field>

            <field name="model_id" ref="model_openacademy_course"/>

            <field name="groups" eval="[(4, ref('openacademy.group_manager'))]"/>

            <field name="perm_read" eval="0"/>

            <field name="perm_write" eval="1"/>

            <field name="perm_create" eval="0"/>

            <field name="perm_unlink" eval="1"/>

            <field name="domain_force">

                ['|', ('responsible_id','=',False),

                      ('responsible_id','=',user.id)]

            </field>

        </record>

    </data></odoo>

wizards

向导通过动态形式描述与用户(或对话框)的交互式会话。一个向导只是一个扩展类TransientModel而不是 类的模型Model。该类 TransientModel扩展Model 和重用其现有的所有机制,具有以下特点:

· 向导记录不是永久性的它们会在一段时间后自动从数据库中删除。这就是为什么它们被称为 短暂的原因

· 向导模型不需要显式访问权限:用户拥有向导记录的所有权限。

· 向导记录可以通过many2one领域引用常规记录或向导记录,但经常记录无法通过many2one场参考向导记录。

我们希望创建一个向导,允许用户为特定会话创建与会者,或者同时创建一个会话列表。

行使

定义向导

创建与会话 模型具有many2one关系的向导模型,并与Partner模型建立许多关系。

添加新文件openacademy/wizard.py

openacademy/__init__.py

from . import controllersfrom . import models

from . import partnerfrom . import wizard

openacademy/wizard.py

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

from odoo import models, fields, api

class Wizard(models.TransientModel):

_name = 'openacademy.wizard'

    session_id = fields.Many2one('openacademy.session',

        string="Session", required=True)

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

启动向导

向导由ir.actions.act_window记录启动,字段 target设置为该值new。后者将向导视图打开到弹出窗口。操作可以由菜单项触发。

还有另一种启动向导的方法:使用ir.actions.act_window 上面的记录,但是有一个额外的字段src_model可以在动作可用的上下文中指定。向导将出现在模型的上下文操作中,主视图上方。由于ORM中有一些内部钩子,所以这种动作在XML中用标签声明act_window

“act_window id =”launch_the_wizard“

            name =”启动向导“

            src_model =”context.model.name“

            res_model =”wizard.model.name“

            view_mode =”form“

            target =”new“

            key2 =”client_action_multi“/>

向导使用常规视图,其按钮可以使用属性 special="cancel"来关闭向导窗口而不保存。

行使

启动向导

1. 为向导定义窗体视图。

2. 添加操作以在会话模型的上下文中启动它。

3. 在向导中定义会话字段的默认值使用上下文参数self._context来检索当前会话。

openacademy/wizard.py

class Wizard(models.TransientModel):

    _name = 'openacademy.wizard'

def _default_session(self):

        return self.env['openacademy.session'].browse(self._context.get('active_id'))

    session_id = fields.Many2one('openacademy.session',

        string="Session", required=True, default=_default_session)

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml

        <menuitem id="session_menu" name="Sessions"

                  parent="openacademy_menu"

                  action="session_list_action"/>

        <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_id"/>

                        <field name="attendee_ids"/>

                    </group>

                </form>

            </field>

        </record>

        <act_window id="launch_session_wizard"

                    name="Add Attendees"

                    src_model="openacademy.session"

                    res_model="openacademy.wizard"

                    view_mode="form"

                    target="new"

                    key2="client_action_multi"/>

    </data></odoo>

行使

注册与会者

向向导添加按钮,并实现相应的方法,将与会者添加到给定的会话。

openacademy/views/openacademy.xml

                        <field name="attendee_ids"/>

                    </group>

                    <footer>

                        <button name="subscribe" type="object"

                                string="Subscribe" class="oe_highlight"/>

                        Or

                        <button special="cancel" string="Cancel"/>

                    </footer>

                </form>

            </field>

        </record>

openacademy/wizard.py

    session_id = fields.Many2one('openacademy.session',

        string="Session", required=True, default=_default_session)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

    @api.multi

    def subscribe(self):

        self.session_id.attendee_ids |= self.attendee_ids

        return {}                         

行使

向与会者注册多个会话

修改向导模型,以便与会者可以注册到多个会话。

openacademy/views/openacademy.xml

                <form string="Add Attendees">

                    <group>

                        <field name="session_ids"/>

                        <field name="attendee_ids"/>

                    </group>

                    <footer>

                        <button name="subscribe" type="object"

openacademy/wizard.py

class Wizard(models.TransientModel):

    _name = 'openacademy.wizard'

def _default_sessions(self):

        return self.env['openacademy.session'].browse(self._context.get('active_ids'))

    session_ids = fields.Many2many('openacademy.session',

        string="Sessions", required=True, default=_default_sessions)

    attendee_ids = fields.Many2many('res.partner', string="Attendees")

 

    @api.multi

def subscribe(self):

        for session in self.session_ids:

            session.attendee_ids |= self.attendee_ids

        return {}

        

国际化

每个模块可以在i18n目录中提供自己的翻译,通过使用名为LANG.po的文件,其中LANG是语言的语言代码,或者语言和国家组合不同(例如pt.popt_BR.po)时。Odoo会为所有已启用语言自动加载翻译。开发人员在创建模块时总是使用英文,然后使用Odoogettext POT导出功能(设置翻译导入/导出导出翻译,而不指定语言)导出模块术语,以创建模块模板POT文件,然后导出翻译PO文件。许多IDE具有用于编辑和合并PO / POT文件的插件或模式。

小费

Odoo生成的便携式对象文件在Transifex上发布 ,可以轻松翻译软件。

|- idea/ # The module directory

   |- i18n/ # Translation files

      | - idea.pot # Translation Template (exported from Odoo)

      | - fr.po # French translation

      | - pt_BR.po # Brazilian Portuguese translation

      | (...)

小费

默认情况下,OdooPOT导出仅提取XML文件中的标签或Python代码中的字段定义内容,但是通过使用函数odoo._() (例如_("Label"))可以将任何Python字符串进行翻译,

行使

翻译一个模块

为您的Odoo安装选择第二种语言。使用Odoo提供的设施翻译您的模块。

1. 创建一个目录 openacademy/i18n/

2. 安装你想要的语言( 管理翻译加载官方翻译

3. 同步翻译术语(管理翻译应用术语同步翻译

4. 通过导出(管理翻译- >导入/导出导出翻译)创建模板翻译文件, 而不指定语言,保存openacademy/i18n/

5. 通过导出(Administration‣Translations‣导入/导出导出翻译)创建翻译文件 并指定一种语言。保存在 openacademy/i18n/

6. 打开导出的翻译文件(使用基本的文本编辑器或专用的PO文件编辑器,例如POEdit并翻译缺少的术语

7. 在其中models.py,为函数添加import语句, odoo._并将缺少的字符串标记为可翻译

8. 重复步骤3-6

openacademy/models.py

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

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"))

报告

印刷报告

Odoo 8.0附带了一个基于QWeb Twitter BootstrapWkhtmltopdf的新的报告引擎。

报告是一个组合的两个要素:

ir.actions.report.xml,为其提供<report>快捷方式元素,它为报表设置各种基本参数(默认类型,报表是否应在生成后将其保存到数据库中...

<report

    id="account_invoices"

    model="account.invoice"

    string="Invoices"

    report_type="qweb-pdf"

    name="account.report_invoice"

    file="account.report_invoice"

    attachment_use="True"

attachment="(object.state in ('open','paid')) and

        ('INV'+(object.number or '').replace('/','')+'.pdf')"/>

实际报告的标准QWeb视图

<t t-call="report.html_container">

    <t t-foreach="docs" t-as="o">

        <t t-call="report.external_layout">

            <div class="page">

                <h2>Report title</h2>

            </div>

        </t>

</t>

</t>

 

the standard rendering context provides a number of elements, the most

important being:

 

``docs``

    the records for which the report is printed

``user``

    the user printing the report

· 

由于报表是标准网页,因此可以通过网址获得,并且可以通过此URL操纵输出参数,例如,通过http// localhost8069 / report / html / account.report_invoice可以使用HTML版本的“ 发票报告 / 1(如果account已安装)和PDF版本通过http// localhost8069 / report / pdf / account.report_invoice / 1

危险

如果您的PDF报告似乎缺少样式(即文本显示,但样式/布局与html版本不同),可能您的wkhtmltopdf进程无法访问您的Web服务器以进行下载。

如果检查您的服务器日志,并看到生成PDF报告时没有下载CSS样式,那么这绝对是问题所在。

wkhtmltopdf过程将使用web.base.url系统参数的根路径如果您的服务器位于后面的一些类型的代理,这可能是不可到达的所有链接的文件,但此参数是每个管理员的登录时间自动更新。您可以通过添加以下系统参数来解决此问题:

· report.url指向可从您的服务器访问的URL(可能http://localhost:8069或类似的东西)。它将仅用于此特定目的。

· web.base.url.freeze,当设置为True,将停止自动更新web.base.url

行使

为会话模型创建报告

对于每个会话,它应显示会话的名称,其开始和结束,并列出会话的与会者。

openacademy/__manifest__.py

        'views/openacademy.xml',

        'views/partner.xml',

        'views/session_workflow.xml',

        'reports.xml',

    ],

    # only loaded in demonstration mode

    'demo': [

openacademy/reports.xml

<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" />

    <template id="report_session_view">

        <t t-call="report.html_container">

            <t t-foreach="docs" t-as="doc">

                <t t-call="report.external_layout">

                    <div class="page">

                        <h2 t-field="doc.name"/>

                        <p>From

 <span t-field="doc.start_date"/>

 To

 <span t-field="doc.end_date"/></p> 

                       <h3>Attendees:</h3>

                        <ul>

                            <t t-foreach="doc.attendee_ids" t-as="attendee">

                                <li><span t-field="attendee.name"/></li>

                            </t>

                        </ul>

                    </div>

                </t>

            </t>

        </t>

    </template>

</data></odoo>

                

仪表板

行使

定义仪表板

定义包含您创建的图形视图,会话日历视图和课程列表视图(可切换到表单视图)的仪表板。该仪表板应通过菜单中的菜单进行显示,并在选择OpenAcademy主菜单时自动显示在Web客户端中。

1. 

创建一个文件openacademy/views/session_board.xml。它应该包含板视图,该视图中引用的操作,打开仪表板的操作以及重新定义主菜单项以添加仪表板操作

注意

可用仪表盘样式是11-11-2 2-11-1-1

2. 更新openacademy/__manifest__.py以引用新的数据文件

openacademy/__manifest__.py

    'version': '0.1',

 

    # any module necessary for this one to work correctly    'depends': ['base', 'board'],

    # always loaded

    'data': [

        'views/openacademy.xml',

        'views/partner.xml',

        'views/session_workflow.xml',        'views/session_board.xml',        'reports.xml',

    ],

    # only loaded in demonstration mode

openacademy/views/session_board.xml

<?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>

网页服务

网络服务模块为所有网络服务提供通用接口:

· XML-RPC

· JSON-RPC

业务对象也可以通过分布式对象机制访问。它们都可以通过客户端界面进行上下文视图修改。

Odoo可以通过XML-RPC / JSON-RPC接口访问,对于哪些库以多种语言存在。

XML-RPC库

以下示例是与Odoo服务器与库进行交互的Python程序xmlrpclib

import xmlrpclib

root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)

uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)

print "Logged in as%s (uid:%d)" % (USER, uid)

 

# Create a new note

sock = xmlrpclib.ServerProxy(root + 'object')

args = {

    'color' : 8,

    'memo' : 'This is a note',

'create_uid': uid,

}

note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)

行使

向客户端添加新服务

编写一个能够将XML-RPC请求发送到运行Odoo(您的或您的教师)的PCPython程序。这个程序应该显示所有的会话和他们相应的座位数。它也应该为其中一个课程创建一个新的课程。

import functools

import xmlrpclib

HOST = 'localhost'

PORT = 8069

DB = 'openacademy'

USER = 'admin'

PASS = 'admin'

ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)

# 1. Login

uid = xmlrpclib.ServerProxy(ROOT + 'common').

login(DB,USER,PASS)print "Logged in as%s (uid:%d)" % (USER,uid)

call = functools.partial(

    xmlrpclib.ServerProxy(ROOT + 'object').execute,

    DB, uid, PASS)

# 2. Read the sessions

sessions = call('openacademy.session','search_read', [], ['name','seats'])for session in sessions:

print "Session%s (%s seats)" % (session['name'], session['seats'])

# 3.create a new session

session_id = call('openacademy.session', 'create', {

    'name' : 'My session',

    'course_id' : 2,})

 

而不是使用硬编码的课程ID,代码可以通过名称查找课程:

# 3.create a new session for the "Functional" course

course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]

session_id = call('openacademy.session', 'create', {

    'name' : 'My session',

    'course_id' : course_id,})

rse_id'  course_id ,})

JSON-RPC库

以下示例是使用标准Pythonurllib2Odoo服务器进行交互的Python程序json

import json

import random

import urllib2

def json_rpc(url, method, params):

    data = {

        "jsonrpc": "2.0",

        "method": method,

        "params": params,

        "id": random.randint(0, 1000000000),

    }

    req = urllib2.Request(url=url, data=json.dumps(data), headers={

        "Content-Type":"application/json",

    })

    reply = json.load(urllib2.urlopen(req))

    if reply.get("error"):

        raise Exception(reply["error"])

    return reply["result"]

def call(url, service, method, *args):

    return json_rpc(url, "call", {"service": service, "method": method, "args": args})

# log in the given database

url = "http://%s:%s/jsonrpc" % (HOST, PORT)uid = call(url, "common", "login", DB, USER, PASS)

# create a new note

args = {

    'color' : 8,

    'memo' : 'This is another note',

'create_uid': uid,}

note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)

         

这是同一个程序,使用库 jsonrpclib

import jsonrpclib

# server proxy object

url = "http://%s:%s/jsonrpc" % (HOST, PORT)

server = jsonrpclib.Server(url)

# log in the given database

uid = server.call(service="common", method="login", args=[DB, USER, PASS])

# helper function for invoking model methods

def invoke(model, method, *args):

    args = [DB, uid, PASS, model, method] + list(args)

    return server.call(service="object", method="execute", args=args)

# create a new note

args = {

    'color' : 8,

    'memo' : 'This is another note',

'create_uid': uid,}

note_id = invoke('note.note', 'create', args)

    

XML-RPCJSON-RPC可以轻松实现示例。

注意

有许多各种语言的高级API可以访问Odoo系统,而无需明确地通过XML-RPCJSON-RPC,例如:

· https://github.com/akretion/ooor

· https://github.com/syleam/openobject-library

· https://github.com/nicolas-van/openerp-client-lib

· http://pythonhosted.org/OdooRPC

· https://github.com/abhishek-jaiswal/php-openerp-lib

[1]可以disable the automatic creation of some fields

[2]编写原始SQL查询是可行的,但需要小心,因为它绕过了所有的Odoo身份验证和安全机制。

 

原创粉丝点击