构建一个模块¶
危险
This tutorial is outdated. We recommend reading Server framework 101 instead.
警告
本教程需要 已安装Odoo
启动/停止Odoo服务器¶
Odoo使用客户端/服务器架构,其中客户端通过RPC访问Odoo服务器。
通常情况下,业务逻辑和扩展是在服务器端执行的,但是可以向客户端添加支持客户端功能(例如交互式地图等新的数据表示方式)。
为了启动服务器,只需在shell中调用命令 odoo-bin,如果需要,添加文件的完整路径:
odoo-bin
停止服务器的方法是从终端中按两次 Ctrl-C
,或者杀死相应的操作系统进程。
构建一个Odoo模块¶
服务器和客户端扩展都打包为 模块,可以选择性地加载到 数据库 中。
Odoo模块可以向Odoo系统添加全新的业务逻辑,也可以修改和扩展现有的业务逻辑:可以创建一个模块将您国家的会计规则添加到Odoo的通用会计支持中,而下一个模块则添加了对公交车队实时可视化的支持。
Odoo 中的一切都始于模块,也以模块结束。
模块的组成¶
一个Odoo模块可以包含多个元素:
- 业务对象
这些资源被声明为Python类,根据其配置,它们会被Odoo自动持久化。
- Object views
业务对象UI显示的定义
- Data files
XML或CSV文件声明模型元数据:
- Web controllers
处理来自Web浏览器的请求
- 静态网页数据
网站或 Web 界面使用的图片、CSS 或 JavaScript 文件
模块结构¶
每个模块都是 模块目录 中的一个目录。通过使用 --addons-path
选项来指定模块目录。
小技巧
大多数命令行选项也可以使用 配置文件 进行设置
一个Odoo模块通过其 manifest 声明。
模块也是一个 Python package,它包含一个 __init__.py
文件,其中包含了模块中各个 Python 文件的导入指令。
例如,如果该模块只有一个 mymodule.py
文件, __init__.py
可能包含以下内容:
from . import mymodule
Odoo 提供了一种机制来帮助设置新模块, odoo-bin 有一个子命令 scaffold 来创建一个空模块:
$ odoo-bin scaffold <module name> <where to put it>
该命令会为您的模块创建一个子目录,并自动创建一堆标准模块文件。其中大部分只包含注释代码或XML。这些文件的大多数用法将在本教程中解释。
Exercise
模块创建
使用上面的命令行创建一个空的Open Academy模块,并将其安装在Odoo中。
对象关系映射¶
Odoo 的一个关键组件是 ORM 层。这一层避免了手动编写大部分 SQL,并提供了可扩展性和安全性服务2。
业务对象被声明为扩展了 Model
的 Python 类,将它们集成到自动化持久化系统中。
模型可以通过在其定义中设置一些属性来进行配置。最重要的属性是: _name
,它是必需的,并定义了模型在Odoo系统中的名称。下面是一个模型的最小完整定义:
from odoo import models
class MinimalModel(models.Model):
_name = 'test.model'
模型字段¶
字段用于定义模型可以存储的内容和位置。字段被定义为模型类的属性:
from odoo import models, fields
class LessMinimalModel(models.Model):
_name = 'test.model2'
name = fields.Char()
常用属性¶
与模型本身一样,可以通过传递配置属性作为参数来配置其字段:
name = fields.Char(required=True)
所有字段都有一些可用的属性,以下是最常见的属性:
string
(unicode
, default: field’s name)用户界面中字段的标签(用户可见)。
required
(bool
, default:False
)如果
True
,字段不能为空,它必须要么有一个默认值,要么在创建记录时始终给定一个值。help
(unicode
, default:''
)长格式,为用户在UI中提供帮助工具提示。
index
(bool
, default:False
)请求Odoo在该列上创建一个
数据库索引
_。
简单字段¶
有两种广泛的字段类别:”简单”字段是直接存储在模型表中的原子值,而”关系”字段则链接记录(同一模型或不同模型的记录)。
保留字段¶
Odoo 在所有模型中创建了一些字段1。这些字段由系统管理,不应该被写入。如果有用或必要,可以读取它们:
特殊字段¶
默认情况下,Odoo还要求所有模型上有一个 name
字段,用于各种显示和搜索行为。用于这些目的的字段可以通过设置 _rec_name
来覆盖。
Exercise
定义一个模型
在 openacademy 模块中定义一个新的数据模型 Course 。一个课程有一个标题和一个描述。课程必须有一个标题。
数据文件¶
Odoo是一个高度数据驱动的系统。虽然行为是使用Python_代码定制的,但模块的价值部分在于加载时设置的数据。
小技巧
一些模块仅用于将数据添加到Odoo中
模块数据通过 data files 声明,这些是带有 <record>
元素的 XML 文件。每个 <record>
元素都会创建或更新一个数据库记录。
<odoo>
<record model="{model name}" id="{record identifier}">
<field name="{a field name}">{a value}</field>
</record>
</odoo>
model
is the name of the Odoo model for the record.id
is an external identifier, it allows referring to the record (without having to know its in-database identifier).<field>
元素有一个name
属性,它是模型中字段的名称(例如description
)。它们的内容是字段的值。
数据文件必须在清单文件中声明才能加载,它们可以在 'data'
列表(始终加载)或 'demo'
列表(仅在演示模式下加载)中声明。
Exercise
定义演示数据
创建演示数据,向 Courses 模型中填充一些演示课程。
基本视图¶
Views define the way the records of a model are displayed. Each type of view represents a mode of visualization (a list of records, a graph of their aggregation, …). Views can either be requested generically via their type (e.g. a list of partners) or specifically via their id. For generic requests, the view with the correct type and the lowest priority will be used (so the lowest-priority view of each type is the default view for that type).
View inheritance allows altering views declared elsewhere (adding or removing content).
通用视图声明¶
视图被声明为模型 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>, <list>, <graph>, ... -->
</field>
</record>
危险
视图的内容是 XML。
因此, arch
字段必须声明为 type="xml"
才能正确解析。
list views¶
list views, also called list views, display records in a tabular form.
Their root element is <list>
. The simplest form of the list view simply
lists all the fields to display in the table (each field as a column):
<list string="Idea list">
<field name="name"/>
<field name="inventor_id"/>
</list>
表单视图¶
表单用于创建和编辑单个记录。
它们的根元素是 <form>
。它们由高级结构元素(组、笔记本)和交互元素(按钮和字段)组成:
<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>
Exercise
使用XML自定义表单视图
为课程对象创建自己的表单视图。显示的数据应包括:课程名称和描述。
Exercise
笔记本电脑
在课程表单视图中,将描述字段放在选项卡下面,这样以后添加其他选项卡包含额外信息会更容易。
表单视图也可以使用纯HTML进行更灵活的布局:
<form string="Idea Form">
<header>
<button string="Confirm" type="object" name="action_confirm"
invisible="state != 'draft'" class="oe_highlight" />
<button string="Mark as done" type="object" name="action_done"
invisible="state != 'confirmed'" class="oe_highlight"/>
<button string="Reset to draft" type="object" name="action_draft"
invisible="state not in ['confirmed', 'done']" />
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only" string="Idea Name" />
<h1><field name="name" /></h1>
</div>
<separator string="General" colspan="2" />
<group colspan="2" col="2">
<field name="description" placeholder="Idea description..." />
</group>
</sheet>
</form>
搜索视图¶
搜索视图定制与列表视图(和其他聚合视图)相关联的搜索字段。它们的根元素是 <search>
,由定义可以搜索哪些字段的字段组成:
<search>
<field name="name"/>
<field name="inventor_id"/>
</search>
如果模型没有搜索视图,Odoo会生成一个只允许在 name
字段上进行搜索的搜索视图。
Exercise
搜索课程
允许根据课程标题或描述搜索课程。
模型之间的关系¶
一个模型中的记录可能与另一个模型中的记录相关联。例如,销售订单记录与包含客户数据的客户记录相关联;它还与其销售订单行记录相关联。
Exercise
创建一个会话模型
对于Open Academy模块,我们考虑一个 sessions 模型:一个session是在给定时间为给定受众教授的课程的发生。
创建一个 sessions 模型。一个 session 有一个名称,一个开始日期,一个持续时间和一个座位数。添加一个操作和一个菜单项来显示它们。通过一个菜单项使新模型可见。
关联字段¶
关系字段链接记录,可以是同一模型的记录(层次结构),也可以是不同模型之间的记录。
关系型字段类型有:
Many2one(other_model, ondelete='set null')
一个简单的链接到另一个对象:
print(foo.other_id.name)
另请参阅
One2many(other_model, related_field)
一个虚拟关系,是
Many2one
的反向关系。一个One2many
表现为记录的容器,访问它会得到一组(可能为空的)记录:for other in foo.other_ids: print(other.name)
Many2many(other_model)
双向多关系,一侧的任何记录都可以与另一侧的任意数量的记录相关联。它的行为类似于记录的容器,访问它也可能会导致一个空的记录集合::
for other in foo.other_ids: print(other.name)
Exercise
多对一关系
使用 many2one,修改 Course 和 Session 模型以反映它们与其他模型的关系:
一个课程有一个 负责人 用户;该字段的值是内置模型
res.users
的记录。一个会话有一个 instructor;该字段的值是内置模型
res.partner
的记录。一个会话与一个 课程 相关联;该字段的值是
openacademy.course
模型的记录,并且是必需的。调整视图。
Exercise
反向一对多关系
使用反向关系字段one2many,修改模型以反映课程和会话之间的关系。
Exercise
多个 many2many 关系
使用关系字段 many2many,修改 Session 模型以将每个会话与一组 attendees 关联起来。参与者将由合作伙伴记录表示,因此我们将与内置模型 res.partner
相关联。相应地调整视图。
继承¶
模型继承¶
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
的主体替换匹配的元素,在新主体中用原始元素替换任何$0
节点出现before
在匹配的元素之前插入
xpath
的内容作为兄弟元素after
在匹配的元素之后,将
xpaths
的内容作为兄弟元素插入attributes
使用
xpath
的主体中的特殊attribute
元素来更改匹配元素的属性
小技巧
当匹配单个元素时,可以直接在要查找的元素上设置 position
属性。下面的两个继承都会得到相同的结果。
<xpath expr="//field[@name='description']" position="after"> <field name="idea_ids" /> </xpath> <field name="description" position="after"> <field name="idea_ids" /> </field>
Exercise
修改现有内容
使用模型继承,修改现有的 Partner 模型,添加一个布尔类型的
instructor
字段,以及一个与会话-伙伴关系对应的多对多字段使用视图继承,在合作伙伴表单视图中显示这些字段
域名¶
在Odoo中, 搜索域 是编码记录条件的值。一个域是一个标准列表,用于选择模型记录的子集。每个标准是一个三元组,包含字段名、操作符和值。
例如,当应用于 Product 模型时,以下领域选择所有单价超过 1000 的 services:
[('product_type', '=', 'service'), ('unit_price', '>', 1000)]
默认情况下,条件会隐式使用 AND 进行组合。逻辑运算符 &
(AND)、 |
(OR)和 !
(NOT)可用于显式组合条件。它们在前缀位置使用(运算符插入在其参数之前而不是之间)。例如,要选择“是服务 OR 单价 NOT 在1000和2000之间”的产品:
['|',
('product_type', '=', 'service'),
'!', '&',
('unit_price', '>=', 1000),
('unit_price', '<', 2000)]
可以在关系字段中添加 domain
参数,以限制在客户端界面中选择记录时的有效关系记录。
Exercise
关系字段上的域
当选择 Session 的讲师时,只有讲师( instructor
设置为 True
的合作伙伴)才应该可见。
Exercise
更复杂的域名
创建新的合作伙伴类别 Teacher / Level 1 和 Teacher / Level 2。一个会话的讲师可以是讲师或教师(任何级别)。
计算字段和默认值¶
到目前为止,字段直接存储在数据库中并直接从数据库中检索。字段也可以是 计算字段 。在这种情况下,字段的值不是从数据库中检索出来的,而是通过调用模型的一个方法来实时计算的。
要创建一个计算字段,创建一个字段并将其属性 compute
设置为一个方法的名称。计算 方法应该简单地将字段的值设置为在 self
中的每个记录上计算。
危险
self
is a collection
对象 self
是一个 recordset,即有序的记录集合。它支持标准的 Python 集合操作,如 len(self)
和 iter(self)
,还支持额外的集合操作,如 recs1 + recs2
。
迭代 self
逐个给出记录,其中每个记录本身是一个大小为1的集合。您可以使用点表示法,如 record.name
,访问/分配单个记录上的字段。
import random
from odoo import models, fields, api
class ComputedModel(models.Model):
_name = 'test.computed'
name = fields.Char(compute='_compute_name')
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
Exercise
计算字段
将已选座位的百分比添加到 Session 模型中
Display that field in the list and form views
将该字段显示为进度条
默认值¶
任何字段都可以给定默认值。在字段定义中,添加选项 default=X
,其中 X
是 Python 字面值(布尔值、整数、浮点数、字符串)或一个接受记录集并返回值的函数:
name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
注解
对象 self.env
提供了访问请求参数和其他有用信息的方法:
self.env.cr
或self._cr
是数据库 cursor 对象;它用于查询数据库
self.env.uid
或self._uid
是当前用户的数据库ID
self.env.user
is the current user’s record
self.env.context
或self._context
是上下文字典
self.env.ref(xml_id)
returns the record corresponding to an XML id
self.env[model_name]
returns an instance of the given model
触发事件¶
“onchange”机制提供了一种方式,使得客户端界面可以在用户填写字段值时更新表单,而不需要将任何内容保存到数据库中。
例如,假设一个模型有三个字段 amount
、 unit_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
行为是内置的,可以通过与 Session 表单进行交互来看到:更改座位数或参与者数量, taken_seats
进度条会自动更新。
Exercise
警告
添加一个明确的 onchange 以警告无效值,例如负数座位数或参与者超过座位数。
模型约束¶
Odoo 提供了两种设置自动验证不变量的方法: Python 约束
和 SQL 约束
。
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
Exercise
添加 Python 约束
添加一个约束条件,检查讲师是否在自己的会议参与者中。
SQL 约束通过模型属性 _sql_constraints
定义。后者被赋值为 字符串三元组列表 (name, sql_definition, message)
,其中 name
是有效的 SQL 约束名称, sql_definition
是 table_constraint 表达式, message
是错误消息。
Exercise
练习 6 - 添加重复选项
由于我们为课程名称的唯一性添加了约束,因此无法再使用“复制”功能(
)。重新实现您自己的“复制”方法,允许复制课程对象,并将原始名称更改为“[原始名称]的副本”。
高级视图¶
list views¶
list views can take supplementary attributes to further customize their behavior:
decoration-{$name}
允许根据相应记录的属性更改行文本的样式。
值是Python表达式。对于每个记录,表达式将使用记录的属性作为上下文值进行评估,如果为
true
,则将应用相应的样式到行中。以下是上下文中其他可用的值:uid
: the id of the current user,today
: the current local date as a string of the formYYYY-MM-DD
,now
: same astoday
with the addition of the current time. This value is formatted asYYYY-MM-DD hh:mm:ss
.
{$name}
可以是bf
(font-weight: bold
),it
(font-style: italic
), 或任何 bootstrap contextual color (danger
,info
,muted
,primary
,success
或warning
).<list string="Idea Categories" decoration-info="state=='draft'" decoration-danger="state=='trashed'"> <field name="name"/> <field name="state"/> </list>
editable
Either
"top"
or"bottom"
. Makes the list view editable in-place (rather than having to go through the form view), the value is the position where new rows appear.
Exercise
列表着色
Modify the Session list view in such a way that sessions lasting less than 5 days are colored blue, and the ones lasting more than 15 days are colored red.
日历¶
将记录显示为日历事件。它们的根元素是 <calendar>
,最常见的属性是:
color
用于 颜色分割 的字段名称。颜色会自动分配给事件,但是具有相同颜色段的事件(其
@color
字段具有相同值的记录)将被赋予相同的颜色。date_start
记录事件开始日期/时间的字段
date_stop
(optional)记录事件结束日期/时间的字段
string
记录的字段,用于定义每个日历事件的标签
<calendar string="Ideas" date_start="invent_date" color="inventor_id">
<field name="name"/>
</calendar>
Exercise
日历视图
为 Session 模型添加一个日历视图,使用户能够查看与 Open Academy 相关的事件。
搜索视图¶
搜索视图中的 <field>
元素可以具有 @filter_domain
,它会覆盖为给定字段进行搜索生成的域。在给定的域中, self
表示用户输入的值。在下面的示例中,它用于在 name
和 description
两个字段上进行搜索。
搜索视图也可以包含 <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
字段为搜索字段设置默认值:形式为 search_default_field_name
的上下文键将使用提供的值初始化 field_name。搜索过滤器必须具有可选的 @name
,以具有默认值并作为布尔值行为 (它们只能默认启用)。
Exercise
搜索视图
在课程搜索视图中添加一个按钮,用于筛选当前用户负责的课程。默认情况下,使其被选中。
添加一个按钮,按负责用户分组课程。
甘特图¶
警告
甘特图视图需要 web_gantt 模块,该模块在企业版中是存在的 企业版 版本。
水平条形图通常用于显示项目规划和进展,它们的根元素是 <gantt>
。
<gantt string="Ideas"
date_start="invent_date"
date_stop="date_finished"
progress="progress"
default_group_by="inventor_id" />
Exercise
甘特图
添加一个甘特图,使用户可以查看与Open Academy模块相关联的会话安排。会话应按教练分组。
图形视图¶
图形视图允许对模型进行聚合概览和分析,它们的根元素是 <graph>
。
注解
Pivot views (元素 <pivot>
) 是一个多维表格,允许选择过滤器和维度以获取正确的聚合数据集,然后再转到更图形化的概览。透视图与图形视图共享相同的内容定义。
图形视图有4种显示模式,可以使用 @type
属性选择默认模式。
- 默认的 Bar
在条形图中,第一个维度用于定义水平轴上的分组,其他维度定义每个分组内的聚合条形。
默认情况下,柱状图是并排显示的,可以通过在
<graph>
上使用@stacked="True"
来堆叠。- 行号
二维折线图
- 饼图
二维饼图
图形视图包含带有必填的 @type
属性的 <field>
,其取值为:
row
(default)该字段默认应该进行聚合
measure
应该对该字段进行聚合而不是分组
<graph string="Total idea score by Inventor">
<field name="inventor_id"/>
<field name="score" type="measure"/>
</graph>
警告
图形视图对数据库值执行聚合,不支持非存储计算字段。
Exercise
图形视图
在Session对象中添加一个图表视图,显示每个课程的参与者人数,以条形图的形式呈现。
看板¶
用于组织任务、生产流程等等… 它们的根元素是 <kanban>
。
看板视图显示一组卡片,可能分组在列中。每张卡片代表一条记录,每列代表一个聚合字段的值。
例如,项目任务可以按阶段(每列是一个阶段)或按负责人(每列是一个用户)等方式组织。
Kanban 视图将每个卡片的结构定义为表单元素(包括基本的 HTML)和 QWeb模板 的混合。
Exercise
看板视图
添加一个看板视图,按课程分组显示会话(列即为课程)。
安全性¶
必须配置访问控制机制以实现一致的安全策略。
基于组的访问控制机制¶
组是作为模型 res.groups
上的普通记录创建的,并通过菜单定义授予菜单访问权限。然而,即使没有菜单,对象仍然可以间接访问,因此必须为组定义实际的对象级权限(读取、写入、创建、删除)。它们通常通过模块内的 CSV 文件插入。还可以使用字段的组属性限制对视图或对象上的特定字段的访问。
访问权限¶
访问权限被定义为模型 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
Exercise
通过Odoo界面添加访问控制
创建一个名为“John Smith”的新用户。然后创建一个名为“OpenAcademy / Session Read”的组,该组对 Session 模型具有读取访问权限。
Exercise
在您的模块中通过数据文件添加访问控制
使用数据文件,
创建一个名为 OpenAcademy / Manager 的用户组,该组对所有 OpenAcademy 模型具有完全访问权限
使 Session 和 Course 对所有用户可读
记录规则¶
记录规则限制对给定模型的记录子集的访问权限。规则是模型 ir.rule
的一条记录,与模型、一些组(many2many字段)、适用于该限制的权限和一个域相关联。域指定了访问权限受限的记录。
以下是一个规则的示例,它阻止删除不处于 cancel
状态的潜在客户。请注意,字段 groups
的值必须遵循ORM的方法 write()
的相同约定。
<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>
Exercise
记录规则
为课程模型和组 “OpenAcademy / Manager” 添加记录规则,限制对课程负责人的 write
和 unlink
访问权限。如果课程没有负责人,则该组的所有用户都可以修改它。
向导¶
Wizards 描述与用户的交互会话(或对话框)通过动态表单。一个向导只是一个模型,它扩展了类 TransientModel
而不是 Model
。 类 TransientModel
扩展了 Model
并重用了所有现有的机制,具有以下特点:
向导记录不是持久的;它们在一定时间后会自动从数据库中删除。这就是为什么它们被称为“ transient ”。
向导记录可以通过关系字段(many2one或many2many)引用常规记录或向导记录,但常规记录 不能 通过many2one字段引用向导记录。
我们想创建一个向导,允许用户为特定会话或一次性为多个会话创建参与者。
Exercise
定义向导
创建一个向导模型,该模型与 Session 模型具有多对一的关系,并与 Partner 模型具有多对多的关系。
启动向导¶
向导只是具有 target
字段设置为值 new
的 窗口操作,它在单独的对话框中打开视图(通常是 表单)。该操作可以通过菜单项触发,但更常见的是通过按钮触发。
An other way to launch wizards is through the binding_model_id
field of the
action. Setting this field will make the action appear on the views of the model
the action is “bound” to.
<record id="launch_the_wizard" model="ir.actions.act_window">
<field name="name">Launch the Wizard</field>
<field name="res_model">wizard.model.name</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_context_model_ref"/>
</record>
小技巧
虽然向导使用常规视图和按钮,但通常单击表单中的任何按钮都会先保存表单,然后关闭对话框。由于这在向导中通常是不希望的,因此提供了一个特殊属性 special="cancel"
,它可以立即关闭向导而不保存表单。
Exercise
启动向导
为向导定义一个表单视图。
将该操作添加到以 Session 模型为上下文的环境中启动。
在向导中为会话字段定义默认值;使用上下文参数
self._context
来检索当前会话。
Exercise
注册参会人员
向向导中添加按钮,并实现相应的方法,将与会者添加到给定的会话中。
Exercise
注册参与者到多个会议
修改向导模型,使参与者可以注册多个会话。
国际化¶
每个模块都可以在i18n目录中提供自己的翻译,通过使用名为LANG.po的文件,其中LANG是语言的区域代码,或者在语言和国家组合不同时使用(例如pt.po或pt_BR.po)。Odoo会自动加载所有启用的语言的翻译。开发人员在创建模块时始终使用英语,然后使用Odoo的gettext POT导出功能(
,不指定语言),创建模块模板POT文件,然后生成翻译后的PO文件。许多IDE都有用于编辑和合并PO/POT文件的插件或模式。小技巧
Odoo 生成的 Portable Object 文件发布在 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
| (...)
小技巧
默认情况下,Odoo的POT导出仅提取XML文件中的标签或Python代码中的字段定义中的标签,但是任何Python字符串都可以通过使用函数 odoo._`(例如 ``_("Label")`()
)来进行翻译
Exercise
翻译一个模块
选择第二种语言作为您的Odoo安装语言。使用Odoo提供的工具翻译您的模块。
报告¶
打印报告¶
Odoo 使用基于 QWeb模板 、 Twitter Bootstrap
_ 和 Wkhtmltopdf 的报表引擎。
报告是两个元素的组合:
一个
ir.actions.report
,用于配置报表的各种基本参数(默认类型,报表在生成后是否应保存到数据库中等)<record id="account_invoices" model="ir.actions.report"> <field name="name">Invoices</field> <field name="model">account.invoice</field> <field name="report_type">qweb-pdf</field> <field name="report_name">account.report_invoice</field> <field name="report_file">account.report_invoice</field> <field name="attachment_use" eval="True"/> <field name="attachment">(object.state in ('open','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')</field> <field name="binding_model_id" ref="model_account_invoice"/> <field name="binding_type">report</field> </record>
小技巧
Because it largerly a standard action, as with 向导 it is generally useful to add the report as a contextual item on the list and / or form views of the model being reported on via the
binding_model_id
field.在这里,我们还使用了
binding_type
,以便报告出现在 报告 上下文菜单中,而不是 操作 菜单中。技术上没有区别,但将元素放在正确的位置有助于用户。A standard QWeb view for the actual report:
<t t-call="web.html_container"> <t t-foreach="docs" t-as="o"> <t t-call="web.external_layout"> <div class="page"> <h2>Report title</h2> </div> </t> </t> </t>
标准渲染上下文提供了许多元素,其中最重要的是:
docs
打印报表的记录
user
打印报告的用户
由于报表是标准的网页,因此可以通过URL访问,并且可以通过该URL操纵输出参数,例如 发票 报表的HTML版本可通过http://localhost:8069/report/html/account.report_invoice/1(如果安装了 account
)访问,PDF版本可通过http://localhost:8069/report/pdf/account.report_invoice/1访问。
危险
如果你的PDF报告缺少样式(即文本出现但样式/布局与HTML版本不同),可能是因为你的wkhtmltopdf_进程无法访问你的Web服务器以下载它们。
如果您检查服务器日志并发现在生成PDF报告时未下载CSS样式,则很可能是该问题。
wkhtmltopdf 进程将使用 web.base.url
系统参数作为所有链接文件的 根路径 ,但是此参数每次管理员登录时会自动更新。如果您的服务器位于某种代理后面,则可能无法访问。您可以通过添加以下系统参数来解决此问题:
report.url
, 指向从您的服务器可访问的URL(可能是http://localhost:8069
或类似的URL)。它仅用于此特定目的。web.base.url.freeze
, when set toTrue
, will stop the automatic updates toweb.base.url
.
Exercise
为Session模型创建报告
对于每个会话,应该显示会话的名称、开始和结束时间,并列出会话的参与者。
仪表盘¶
Exercise
定义一个仪表盘
定义一个仪表板,包含你创建的图表视图、会话日历视图和课程列表视图(可切换为表单视图)。这个仪表板应该通过菜单项在菜单中可用,并在选择OpenAcademy主菜单时自动在Web客户端中显示。
- 1
可以 禁用某些字段的自动创建
- 2
编写原始的 SQL 查询是可能的,但需要小心,因为它会绕过所有的 Odoo 认证和安全机制。