有用的类和mixin
odoo提供了很多有用的类和混入(mixin)来让开发人员很方便的给对象添加常用功能。下面会描述这些类和混入。
消息特性
消息整合
基本的消息系统
在模型中整合消息系统是很简单的,只需要从mail.thread
继承模型并将对应的字段和widget添加到视图中就可以了。
例:创建一个关于出差的讨论系统
#model
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char()
partner_id = fields.Many2one('res.partner', 'Responsible')
guest_ids = fields.Many2many('res.partner', 'Participants')
#view
<record id="businness_trip_form" model="ir.ui.view">
<field name="name">business.trip.form</field>
<field name="model">business.trip</field>
<field name="arch" type="xml">
<form string="Business Trip">
<!-- Your usual form view goes here
...
Then comes chatter integration -->
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
添加好了聊天系统后,用户可以很方便的在指定模型的任意记录上添加消息或内部备忘录,每条消息自动发送到关注的人,内部备忘录会被发送到对应员工。如果邮件系统配置好了的话还会发送邮件,并可以在邮件里直接回复并同步到相对应的聊天列表。
在服务端,有一些方法可用于发送消息和管理关注者。
1.发布消息
-
message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, content_subtype='html', **kwargs)
在一个会话列表中发布一条新消息,返回新的mail.message
ID
参数:
- body (str) - 消息内容,一般是原始html,会自动进行过滤
- message_type (str) - 与mail_message.type字段对应
- content_subtype (str) - 如果是
plaintext
(纯文本),会被转成html - parent_id (int) - 用于当在消息列表时以私信形式回复时使用,对应父级partner
- attachments (list(tuple(str,str))) - 使用(name,content)元组传送的附件列表,content是base64encode过的
- **kwargs - 其他的参数用于创建新mail.message记录
-
message_post_with_view(views_or_xmlid, **kwargs)
发送一条使用指定id对应视图进行渲染的消息。这个方法只能单独使用。
参数or ir.ui.view record (str)
: 视图的id或数据库记录 -
message_post_with_template(template_id, **kwargs)
使用模板来发送消息
参数:
1.template_id - 用于渲染消息主体的模板id
2.**kwargs - 用于创建mail.compose.message(继承自mail.message) wizzard的参数
2.接收消息
在email在邮件网关中被处理时,下面方法会被调用,email可以是创建新的讨论(通过alias)或回复原来的讨论。覆盖他们可以使用邮件内容来设置对应讨论中的值(如更新时间或email地址等)
-
message_new(msg_dict, custom_values=None)
当收到的消息不属于一个已存在的讨论列表时由message_process
调用,默认是基于相关的模型创建一个新讨论记录,其邮件内容直接被提取用于创建新记录,可以通过覆盖该方法来添加新特性。
参数:
1.msg_dict (dict) - 一个包含email内容和附件的映射表,具体看:message_process, mail.message.parse
2.custom_values (dict) - 可选参数,附加的用于传递给记录创建的create()方法的字段,注意这里面的值可能会覆盖邮件里的内容。
返回:新创建的记录对象的id
-
message_update(msg_dict, update_vals=None)
当一个收到已存在的讨论列表的消息时由message_process
调用,默认用邮件里的update_vals
来更新记录。可通过覆盖方法来添加其他特性。
参数:
- msg_dict (dict) - 一个包含email内容和附件的映射表,具体看:message_process,
- update_vals (dict) - 一个包含记录id和值的映射表用于更新,如果该参数为None或空,则不会进行更新操作
3.关注者管理
-
message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)
添加参与者
参数:
1.partner_ids (list(int)) - 当前记录对应的订阅的伙伴id列表
2.channel_ids (list(int)) - 订阅的渠道列表
3.subtype_ids (list(int)) - 所订阅的子类,默认None
4.force - 当值为true时会根据subtype来创建新关注关系(原关注关系会先删除)
返回:boolean 是否成功
-
message_subscribe_users(user_ids=None, subtype_ids=None)
将message_subscribe封装,只是用用户替代partner
参数:
1.user_ids (list(int)) - 当前记录对应的订阅的用户id列表,如果是None订阅当前用户
2.subtype_ids (list(int)) - 渠道或合作伙伴所订阅的子类id
-
message_unsubscribe(partner_ids=None, channel_ids=None)
将合作伙伴从记录的订阅列表移除
参数:
1.partner_ids (list(int)) - 订阅该记录的伙伴id列表(官方是这样的,也许应该是取消订阅)
2.channel_ids (list(int)) - 订阅该记录的渠道列表(官方是这样的,也许应该是取消订阅)
-
message_unsubscribe_users(user_ids=None)
封装message_unsubscribe,用user替代partner
参数:
1.user_ids (list(int)) - 取消订阅的用户id列表,为空时用当前用户
记录变化
mail
模型有一个强大的字段跟踪系统,可将对应值的变化记录到讨论系统中。为了对字段进行监听,只需要将track_visibility 属性设置为onchange
(当字段值改变时展示在通知中),always
(该值总是会被显示在通知中,一般用于让通知内容更好理解)
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(track_visibility='always')
partner_id = fields.Many2one('res.partner', 'Responsible',
track_visibility='onchange')
guest_ids = fields.Many2many('res.partner', 'Participants')
上例中所有name和负责人的修改会被记录,而name在没改变时也会跟着其他的信息改变显示
子类型
子类型可以对消息进行更细分的控制,给通知进行分类,允许订阅者决定接收哪些分类的通知。
子类型在模块中数据的形式创建,模型有以下字段:
- name (必选) - Char -- 子分类的名字,用于自定义通知时显示
- description - Char -- 展示在消息中的类型描述,为空时使用name替代
- internal - Boolean -- 设置为internal的消息只会在内部显示(base.group_user下的会员)
- parent_id - Many2one -- 将子类型之间相关联(如项目子类与任务子类关联,当用户订阅该项目子类时会自动订阅其关联的任务子类)
- relation_field - Char -- 如当项目子类和任务子类关联时,任务中的project_id 就是关联字段
- res_model - Char -- 子类型所应用的模型,如果是False表示应用到所有模型
- default - Boolean -- 该子类型在订阅时是否自动启用
- sequence - Integer -- 用于在自定义通知界面排序显示
- hidden - Boolean -- 是否在自定义通知界面隐藏该子分类
可以通过覆盖_track_subtype
方法来根据用户可能的兴趣同时订阅多种通知。
_track_subtype(init_values)
给出在记录被更新时所触发的子类型
参数:init_values (dict) -- 记录的原始数据中被更新的部分
返回:所触发子类型的id或False(没触发)
#例:添加一个state字段,并在值发生改变时触发一个子分类通知
#子分类定义
<record id="mt_state_change" model="mail.message.subtype">
<field name="name">Trip confirmed</field>
<field name="res_model">business.trip</field>
<field name="default" eval="True"/>
<field name="description">Business Trip confirmed!</field>
</record>
#覆盖track_subtype方法,当state从draft变为confirmed时,触发一个新的subtype
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(track_visibility='onchange')
partner_id = fields.Many2one('res.partner', 'Responsible',
track_visibility='onchange')
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
track_visibility='onchange')
def _track_subtype(self, init_values):
# init_values contains the modified fields' values before the changes
#
# the applied values can be accessed on the record as they are already
# in cache
self.ensure_one()
if 'state' in init_values and self.state == 'confirmed':
return 'my_module.mt_state_change' # Full external id
return super(BusinessTrip, self)._track_subtype(init_values)
自定义通知
当发送通知给关注人后时,在邮件里添加一个按钮用于直接操作是非常有用的。
通知系统允许通过以下方式来自定义通知模板:
- 展示一个指向对应记录的表单的按钮
- 展示一个用于关注的按钮
- 展示一个取消关注按钮
- 展示自定义动作按钮:指定特定的链接,让用户可以直接在邮件里操作。
该按钮设置可以通过覆盖_notification_recipients
方法来应用到不同的用户组中
_notification_recipients(message, groups)
参数:
1.message (record) -- 当前发送的mail.message
记录
2.groups (list(tuple)) -- 三元组(group_name, group_func,group_data) 列表
group_name
- 只有指定的分组才会被覆盖定义:user(员工组),portal(门户用户),customer(客户)
group_func
- 一个只接收partner参数的函数指针,用于判断接收人是否在指定用户组中
group_data
- 包含通知邮件内容的dict,可有以下key:1.has_button_access-- 是否显示Access Document,对新用户组默认true,门户会员和客户默认false
2.button_access -- 按钮的url和标题的 dict
3.has_button_follow -- 是否显示关注按钮,新用户组默认true,门户会员和客户默认false
4.button_follow -- 按钮的url和标题的 dict
5.has_button_unfollow -- 对已关注的人是否显示取消关注按钮,对新用户组默认true,门户会员和客户默认false
6.button_unfollow -- 按钮的url和标题的 dict
7.actions -- 在通知邮件中展示的操作按钮,每个按钮url和标题的 dict
返回:子类的ID或False
注:url可以通过调用_notification_link_helper
来生成
-
_notification_link_helper(self, link_type, **kwargs)
- 基于当前记录和指定类型生成链接
参数:
link_type (str) -- 生成的链接类型,可以是以下几种:
- view -- 链接到记录的表单视图
- assign -- 将当前登录用户赋值到记录的user_id字段(如果有该字段)
- follow ,unfollow -- 关注、取关
- workflow -- 触发一个工作流信号,工作流信号名需要通过kwarg 的signal传递
- method -- 基于记录调用方法,方法名需要通过kwarg的method传递
- new -- 为新记录打开一个空表单,可以通过action_id参数指定一个明确的操作ID(数据库或外部ID)
返回:对应类型的链接
#在商务旅行状态改变通知中添加一个按钮,该按钮可将状态设置为初始状态,只对Travel Manager 用户组(business.group_trip_manager)可见
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Business Trip'
# Pevious code goes here
def action_cancel(self):
self.write({'state': 'draft'})
def _notification_recipients(self, message, groups):
""" Handle Trip Manager recipients that can cancel the trip at the last
minute and kill all the fun. """
groups = super(BusinessTrip, self)._notification_recipients(message, groups)
self.ensure_one()
if self.state == 'confirmed':
app_action = self._notification_link_helper('method',
method='action_cancel')
trip_actions = [{'url': app_action, 'title': _('Cancel')}]
new_group = (
'group_trip_manager',
lambda partner: bool(partner.user_ids) and
any(user.has_group('business.group_trip_manager')
for user in partner.user_ids),
{
'actions': trip_actions,
})
return [new_group] + groups
覆盖默认
有多种方法来自定义mail.thread
模型的行为,如:
-
_mail_post_access
- Model属性
需要拥有model对应的访问权限,默认需要write权限,也可以设置为read -
用于控制mail.thread功能如自动关注或字段监听的上下文KEY
-
mail_create_nosubscribe - 在创建或发布消息时,不自动关注记录的当前用户
-
mail_create_nolog -- 在创建时不自动记录'Document created' 消息
-
mail_notrack -- 在创建或更新时不自动跟踪字段值的变化
-
tracking_disable -- 在创建或更新时,不触发自动关注、跟踪等
-
mail_auto_delete -- 自动删除邮件通知,默认True
-
mail_notify_force_send -- 如果需要发送的邮件数量在50封内,直接发送不使用队列,默认True
-
mail_notify_user_signature -- 将当前用户标识添加到邮件通知里,默认True
邮件别名
别名是链接到指定记录(一般继承自mail.alias.mixin模型)的可配邮件地址,记录在通过邮件联系时创建,可以让外界更方便的接触系统,允许用户或客户快速新增记录而不需要直接访问odoo系统。
-
别名与收邮件网关
使用收邮件网关时需要正确的配置邮件网关来使用别名,在odoo中只要设置一个域名就可以了。odoo别名有以下优势: -
配置简单-只需要配置一个收件网关,多个别名可共用,不需要系统权限来配置别名
-
更加清楚-在记录中配置而不是子菜单设置中
-
容易在服务端覆盖-Mixin 模型可以在最开始时扩展,让你更容易从邮件中提取数据
-
整合别名支持
别名一般在父级模型上配置,在使用邮件联系时会创建一条记录,如:项目有用于创建任务和问题的别名
可通过继承mail.alias.mixin
来添加别名支持,该mixin会为每条新记录创建一条新的mail.alias记录,如每条project.project
记录创建时会自动初始化一条对应的mail.alias
记录
与mail.thread继承不同,mail.alias.mixin
需要一些声明一些覆盖方法才会有用。用于指定新alias的值: -
get_alias_model_name(vals) 返回别名的模型名,不是基于当前已存在记录的回复会自动创建一个别名模型,值由vals参数决定,在调用新增记录时传给create方法,参数vals为新记录的dict
-
get_alias_values()
返回用于创建或更新别名模型的值,通过为别名的alias_defaults 字段设置一个字典形式默认值来确保新创建的记录被链接到别名的父级,不是必须的。
get_alias_values 覆盖方法让你可以很容易的修改别名的行为。有以下字段可以修改:
- alias_name - Char -- 邮件别名的名字,如想要获取
jobs@example.odoo.com
就给出jobs
- alias_user_id - Many2one (res.users) -- 当基于收到的邮件创建记录时,该记录的所有者,如果没有传的话系统会通过发件人邮箱来找用户,找不到的话就用管理员
- alias_defaults - Text -- 当为当前别名创建新记录时所使用的默认值,是一个python格式字典
- alias_force_thread_id - Integer -- 可选的与收到的消息相关联的线索ID,如果有设置就禁止创建新记录
- alias_contact - Selection -- 发布消息时所使用的策略:everyone(每个人都可发布),partners(只有授权的伙伴能发布),followers(只有关注了的人或关注的渠道会员才可发布)
别名使用的是代理继承,意味着如果别名存在另一张表中,也可以通过父对象获取所有的字段,使得在表单视图中配置别名变的很容易。
#例:在出差模型中添加一个别名用于通过邮件来自动创建花费记录
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Business Trip'
name = fields.Char(track_visibility='onchange')
partner_id = fields.Many2one('res.partner', 'Responsible',
track_visibility='onchange')
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
track_visibility='onchange')
expense_ids = fields.One2many('business.expense', 'trip_id', 'Expenses')
alias_id = fields.Many2one('mail.alias', string='Alias', ondelete="restrict",
required=True)
def get_alias_model_name(self, vals):
""" 声明当收到消息时用于创建记录的model """
return 'business.expense'
def get_alias_values(self):
""" 声明在创建alias时使用的默认值 """
values = super(BusinessTrip, self).get_alias_values()
# alias_defaults 保存一个dictionary返回给所以通过该别名创建的记录
# 在这个例子中,需要将所有花费记录发送到trip别名来与对应的出差模型关联
# values['alias_defaults'] = {'trip_id': self.id}
# 默认只允许该出差模型的关注者发布消息
values['alias_contact'] = 'followers'
return values
class BusinessExpense(models.Model):
_name = 'business.expense'
_inherit = ['mail.thread']
_description = 'Business Expense'
name = fields.Char()
amount = fields.Float('Amount')
trip_id = fields.Many2one('business.trip', 'Business Trip')
partner_id = fields.Many2one('res.partner', 'Created by')
#让用户可以在出差表单中配置别名
<page string="Emails">
<group name="group_alias">
<label for="alias_name" string="Email Alias"/>
<div name="alias_def">
<!-- display a link while in view mode and a configurable field
while in edit mode -->
<field name="alias_id" class="oe_read_only oe_inline"
string="Email Alias" required="0"/>
<div class="oe_edit_only oe_inline" name="edit_alias"
style="display: inline;" >
<field name="alias_name" class="oe_inline"/>
@
<field name="alias_domain" class="oe_inline" readonly="1"/>
</div>
</div>
<field name="alias_contact" class="oe_inline"
string="Accept Emails From"/>
</group>
</page>
#现在我们可以通过表单改变别名地址和可发送邮件到别名的人
#也可以通过覆盖花费模型的`message_new`方法来获取用于创建记录的值
def message_new(self, msg, custom_values=None):
""" Override to set values according to the email.
In this simple example, we simply use the email title as the name
of the expense, try to find a partner with this email address and
do a regex match to find the amount of the expense."""
name = msg_dict.get('subject', 'New Expense')
# Match the last occurence of a float in the string
# Example: '50.3 bar 34.5' becomes '34.5'. This is potentially the price
# to encode on the expense. If not, take 1.0 instead
amount_pattern = '(\d+(\.\d*)?|\.\d+)'
expense_price = re.findall(amount_pattern, name)
price = expense_price and float(expense_price[-1][0]) or 1.0
# find the partner by looking for it's email
partner = self.env['res.partner'].search([('email', 'ilike', email_address)],
limit=1)
defaults = {
'name': name,
'amount': price,
'partner_id': partner.id
}
defaults.update(custom_values or {})
res = super(BusinessExpense, self).message_new(msg, custom_values=defaults)
return res
网页特性
访客跟踪
utm.mixin
类可通过在链接里添加特别的参数来跟踪在线商城或交流,它为model添加了三个字段:
- campaign_id: Many2one - 关联utm.campaign 对象(如Christmas_Special, Fall_Collection等)
- source_id: Many2one - 关联utm.source 对象(如Search Engine, mailing list等)
- medium_id: Many2one - 关联utm.medium对象(如Snail Mail, e-Mail, social network update等)
上述模型只有一个单独的name字段,当客户通过带参数url如http://www.odoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website
访问您的网站时,会自动为该访问设置对应cookie记录,当通过网页创建了一个继承了utm.mixin
的记录时,对应cookie内的记录会自动被更新到记录里。这样就可以在报表中使用campaign/source/medium字段了。
可以通过给model添加一个关联字段来扩展该功能,同时需要扩展tracking_fields()
方法
class UtmMyTrack(models.Model):
_name = 'my_module.my_track'
_description = 'My Tracking Object'
name = fields.Char(string='Name', required=True)
class MyModel(models.Models):
_name = 'my_module.my_model'
_inherit = ['utm.mixin']
_description = 'My Tracked Object'
my_field = fields.Many2one('my_module.my_track', 'My Field')
@api.model
def tracking_fields(self):
result = super(MyModel, self).tracking_fields()
result.append([
# ("URL_PARAMETER", "FIELD_NAME_MIXIN", "NAME_IN_COOKIES")
('my_field', 'my_field', 'odoo_utm_my_field')
])
return result
上面例子会在网页被访问时根据my_field参数创建一个odoo_utm_my_field 名的cookie,utm.mixin对create的覆盖方法会自动去获取该字段的值,如果my_module.my_track记录不存在的话它也会同时被创建。
可以在CRM中的crm.lead模型、HR招聘的hr.applicant模型中找到具体的应用实例
网页可见性
可以很容易的在自己的记录上设置网页可见性,该混入比较容易使用,所以它与mail.Thread一样常用。
比如可以通过可见性控制,让您的网页在您满意之后才对外发布。
通过继承website.published.mixin:
来实现:
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.published.mixin']
该mixin为模型添加两个字段:
- website_published: Boolean - 代表发布状态
- website_url: Char - 通过哪个url来访问
后一个字段是实时计算的,必须在类里实现:
def _compute_website_url(self):
for blog_post in self:
blog_post.website_url = "/blog/%s" % (log_post.blog_id)
一旦该机制起作用,必须在前台和后台模板进行对应修改来让它可用,在后端添加一个按钮:
<button class="oe_stat_button" name="website_publish_button"
type="object" icon="fa-globe">
<field name="website_published" widget="website_button"/>
</button>
在前端,需要进行权限检验以防对普通访客展示编辑按钮:
<div id="website_published_button" class="pull-right"
groups="base.group_website_publisher"> <!-- or any other meaningful group -->
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<t t-set="action" t-value="'blog.blog_post_action'"/>
</t>
</div>
必须通过变量object将对象传递到模板,上例中blog.post 通过blog_post变量传递给qweb引擎,publish_edit 变量使得前端按钮与后端操作相关联,对应的需通过action变量来指定其所对应的后端action id
在mixin中定义好了website_publish_button action并将其动作与对象相关联,如果类定义了一个有效的website_url 方法,当用户点击按钮时它会被跳转到指定的前端页面,这样用户可以直接在前端页面发布。这样就保证了不会由于意外点击发布,如果没有计算方法,website_published 会被触发。
网页元数据
这个mixin让你可以非常容易的注入自定义元数据到前端页面中。
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.seo.metadata', 'website.published.mixin']
上述mixin为model添加三个字段
- website_meta_title: Char -- 用于为页面设置一个附加的标题
- website_meta_description: Char -- 包含一个页面的简要描述(有时用于搜索引擎展示)
- website_meta_keywords: Char -- 包含一系列关键字,让页面能更好的被搜索引擎分类
这些字段可以通过编辑工具栏的推广工具修改。可以让搜索引擎更好的索引你的页面。
其他
客户评分
评分mixin允许发送邮件让客户评分,并自动转换成kanban处理并添加到原有评分上进行统计。
为model添加评分
class MyModel(models.Models):
_name = 'my_module.my_model'
_inherit = ['rating.mixin', 'mail.thread']
user_id = fields.Many2one('res.users', 'Responsible')
partner_id = fields.Many2one('res.partner', 'Customer')
将为model添加以下功能
-
rating.rating
记录会与model的partner_id字段关联 - 如果不用partner_id字段可通过覆盖rating_get_partner_id()来设置
-
rating.rating
会与model的user_id字段对应的partner关联 - 可以不用user_id字段可通过覆盖rating_get_rated_partner_id来设置,注意对应函数需返回res.partner对象
- 聊天系统会显示对应的评分记录
通过邮件发送评分请求
如果需要通过邮件来发送评分请求,只需要生成与相应对象关联的邮件即可。
简单的邮件模板如下:
<record id="rating_my_model_email_template" model="mail.template">
<field name="name">My Model: Rating Request</field>
<field name="email_from">${object.rating_get_rated_partner_id().email or '' | safe}</field>
<field name="subject">Service Rating Request</field>
<field name="model_id" ref="my_module.model_my_model"/>
<field name="partner_to" >${object.rating_get_partner_id().id}</field>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
% set access_token = object.rating_get_access_token()
<p>Hi,</p>
<p>How satsified are you?</p>
<ul>
<li><a href="/rating/${access_token}/10">Satisfied</a></li>
<li><a href="/rating/${access_token}/5">Not satisfied</a></li>
<li><a href="/rating/${access_token}/1">Very unsatisfied</a></li>
</ul>
]]></field>
</record>
这样客户会收到一个邮件,可链接到简单的用于提供反馈的页面。在此基础上可以将评分整合到视图里:
<record id="rating_rating_action_my_model" model="ir.actions.act_window">
<field name="name">Customer Ratings</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,pivot,graph</field>
<field name="domain">[('res_model', '=', 'my_module.my_model'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
</record>
<record id="my_module_my_model_view_form_inherit_rating" model="ir.ui.view">
<field name="name">my_module.my_model.view.form.inherit.rating</field>
<field name="model">my_module.my_model</field>
<field name="inherit_id" ref="my_module.my_model_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(rating_rating_action_my_model)d" type="action"
class="oe_stat_button" icon="fa-smile-o">
<field name="rating_count" string="Rating" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
上例中设置了三种默认视图用于更方便的查看客户评分,可以在项目-项目评分的project.task模型中看到具体的应用实例。
译自odoo官方文档:http://www.odoo.com/documentation/10.0/reference/mixins.html ,不当之处欢迎批评指正。
内容发布自http://www.jianshu.com/u/6fdae8ec06bc,转载请注明出处
网友评论