混入与实用类¶
Odoo 实现了一些实用的类和混入,方便您在对象上添加常用行为。本指南将通过示例和用例详细介绍其中大部分内容。
消息功能¶
消息集成¶
基础消息系统¶
在模型中集成消息功能非常简单:只需继承 mail.thread 模型,并在表单视图中添加消息字段(及其对应的组件),即可快速上手。
Example
让我们创建一个表示出差行程的简单模型。由于组织这类行程通常涉及很多人和大量讨论,我们为该模型添加消息交流支持。
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')
在表单视图中:
<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>
在模型上添加沟通栏支持后,用户可以轻松在模型的任意记录上添加消息或内部备注;每一条都会发送通知(消息发送给所有关注者,内部备注发送给员工 (base.group_user) 用户)。如果您的邮件网关和 catchall 地址配置正确,这些通知将通过电子邮件发送,并且可以直接从您的邮件客户端回复;自动路由系统会将回复路由到正确的会话线程。
在服务端,有一些辅助函数可帮助您轻松发送消息并管理记录的关注者:
发布消息
- message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, **kwargs)¶
在现有会话线程中发布一条新消息,返回新 mail.message 的 ID。
- 参数
body (str) – body of the message, usually raw HTML that will be sanitized
message_type (str) – see mail_message.message_type field
parent_id (int) – handle reply to a previous message by adding the parent partners to the message in case of private discussion
attachments (list(tuple(str,str))) – list of attachment tuples in the form
(name,content), where content is NOT base64 encoded**kwargs – extra keyword arguments will be used as default column values for the new mail.message record
- 返回
ID of newly created mail.message
- 返回类型
int
- message_post_with_view(views_or_xmlid, **kwargs):
使用 view_id 通过 ir.qweb 引擎渲染并发送邮件/发布消息的辅助方法。此方法是独立的,因为模板和消息编辑器中没有任何内容支持批量处理视图。当模板能够处理 ir ui 视图时,此方法可能会被移除。
- 参数
record (str or ir.ui.view) – external id or record of the view that should be sent
- message_post_with_template(template_id, **kwargs)¶
使用模板发送邮件的辅助方法
- 参数
template_id – the id of the template to render to create the body of the message
**kwargs – parameter to create a mail.compose.message wizard (which inherit from mail.message)
接收消息
这些方法会在邮件网关处理新电子邮件时被调用。这些邮件既可能是新会话线程(通过 别名 到达),也可能只是对现有会话线程的回复。重写这些方法可以根据邮件本身的一些值在会话线程的记录上设置值(例如更新日期或电子邮件地址、将 CC 地址添加为关注者等)。
- message_new(msg_dict, custom_values=None)¶
当某个会话线程模型收到新消息且该消息不属于现有会话线程时,由
message_process调用。默认行为是根据从消息中提取的一些基本信息创建对应模型的新记录。可以通过重写此方法实现其他行为。
- 参数
msg_dict (dict) – a map containing the email details and attachments. See
message_processandmail.message.parsefor detailscustom_values (dict) – optional dictionary of additional field values to pass to create() when creating the new thread record; be careful, these values may override any other values coming from the message
- 返回类型
int
- 返回
the id of the newly created thread object
- message_update(msg_dict, update_vals=None)¶
当现有会话线程收到新消息时由
message_process调用。默认行为是使用来自来信电子邮件的update_vals更新记录。可以通过重写此方法实现其他行为。
- 参数
msg_dict (dict) – a map containing the email details and attachments; see
message_processandmail.message.parse()for details.update_vals (dict) – a dict containing values to update records given their ids; if the dict is None or is void, no write operation is performed.
- 返回
True
关注者管理
- message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)¶
将伙伴添加为记录的关注者。
- 参数
partner_ids (list(int)) – IDs of the partners that will be subscribed to the record
channel_ids (list(int)) – IDs of the channels that will be subscribed to the record
subtype_ids (list(int)) – IDs of the subtypes that the channels/partners will be subscribed to (defaults to the default subtypes if
None)force – if True, delete existing followers before creating new one using the subtypes given in the parameters
- 返回
Success/Failure
- 返回类型
bool
- message_unsubscribe(partner_ids=None, channel_ids=None)¶
将伙伴从记录的关注者中移除。
- 参数
partner_ids (list(int)) – IDs of the partners that will be subscribed to the record
channel_ids (list(int)) – IDs of the channels that will be subscribed to the record
- 返回
True
- 返回类型
bool
- message_unsubscribe_users(user_ids=None)¶
message_subscribe 的封装方法,基于用户操作。
- 参数
user_ids (list(int)) – IDs of the users that will be unsubscribed to the record; if None, unsubscribe the current user instead.
- 返回
True
- 返回类型
bool
记录变更¶
mail 模块为字段添加了强大的跟踪系统,允许您在记录的沟通栏中记录特定字段的变更。要为字段添加跟踪,只需将 tracking 属性设置为 True。
Example
让我们跟踪出差行程的名称和负责人的变更:
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
从此以后,行程名称或负责人的每次变更都会在记录上记录一条备注。name 字段也会显示在通知中,以提供更多上下文(即使名称没有变化)。
子类型¶
子类型让您能够更细粒度地控制消息。子类型充当通知的分类系统,允许文档的订阅者自定义其希望接收的通知子类型。
子类型作为数据在您的模块中创建;该模型具有以下字段:
name必填)-Char子类型名称,将显示在通知自定义弹窗中
description-Char将添加到为此子类型发布的消息中的描述。如果为空,则改用名称
internal-Boolean内部子类型的消息仅员工可见,即
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) – the original values of the record; only modified fields are present in the dict
- 返回
a subtype’s full external id or False if no subtype is triggered
Example
让我们在示例类上添加一个 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 时使用我们崭新的子类型:
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
tracking=True)
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 self.env.ref('my_module.mt_state_change')
return super(BusinessTrip, self)._track_subtype(init_values)
自定义通知¶
在向关注者发送通知时,在模板中添加按钮以便直接从电子邮件执行快速操作会非常有用。即使是一个直接链接到记录表单视图的简单按钮也很有用;但在大多数情况下,您不希望向门户用户显示这些按钮。
通知系统允许通过以下方式自定义通知模板:
显示 访问按钮 (Access Buttons):这些按钮显示在通知电子邮件顶部,允许收件人直接访问记录的表单视图
显示 关注按钮 (Follow Buttons):这些按钮允许收件人直接快速订阅该记录
显示 取消关注按钮 (Unfollow Buttons):这些按钮允许收件人直接快速取消订阅该记录
显示 自定义操作按钮 (Custom Action Buttons):这些按钮会调用特定路由,让您可以直接从电子邮件执行一些实用操作(例如将线索转化为商机、为费用经理验证费用报销单等)
这些按钮设置可应用于不同的群组,您可以通过重写函数 _notify_get_groups 自行定义这些群组。
- _notify_get_groups(message, groups)¶
根据已更新的值,给出记录上的变更所触发的子类型。
- 参数
message (record) –
mail.messagerecord currently being sentgroups (list(tuple)) –
形如 (group_name, group_func,group_data) 的元组列表,其中:
- group_name
是一个标识符,仅用于能够重写和操作群组。默认群组有
user关联员工用户的收件人)、portal关联门户用户的收件人)和customer未关联任何用户的收件人)。重写用法的一个例子是添加一个关联到 res.groups(如 Hr Officers)的群组,以便为其设置特定的操作按钮。- group_func
是一个以伙伴记录为参数的函数指针。该方法将应用于收件人,以判断他们是否属于某个给定的群组。仅保留第一个匹配的群组。求值顺序即列表顺序。
- group_data
是一个包含通知电子邮件参数的字典,其可能的键 - 值如下:
- has_button_access
是否在电子邮件中显示 Access <Document>。对新群组默认为 True,对 portal / customer 为 False。
- button_access
包含按钮 url 和 title 的字典
- has_button_follow
是否在电子邮件中显示 Follow(如果收件人当前未关注该会话线程)。对新群组默认为 True,对 portal / customer 为 False。
- button_follow
包含按钮 url 和 title 的字典
- has_button_unfollow
是否在电子邮件中显示 Unfollow(如果收件人当前正在关注该会话线程)。对新群组默认为 True,对 portal / customer 为 False。
- button_unfollow
包含按钮 url 和 title 的字典
- actions
要在通知电子邮件中显示的操作按钮列表。每个操作是一个包含按钮 url 和 title 的字典。
- 返回
a subtype’s full external id or False if no subtype is triggered
通过调用 _notify_get_action_link() 函数可以自动生成动作列表中的链接:
- _notify_get_action_link(self, link_type, **kwargs)¶
在当前记录上为给定类型生成链接(若设置了关键字参数
model和res_id,则在指定记录上生成)。- 参数
link_type (str) –
要生成的链接类型;可以是以下值之一:
view记录表单视图的链接
assign将当前登录用户指派给记录的
user_id字段(如果存在)follow顾名思义
unfollow顾名思义
method调用记录上的方法;方法名应通过关键字参数
method提供new为新记录打开空白表单视图;你可以通过关键字参数
action_id提供其 id(数据库 id 或完全解析的外部 id)来指定特定动作
- 返回
link of the type selected for the record
- 返回类型
str
Example
让我们在商务差旅行程状态变更通知中添加一个自定义按钮;该按钮会将状态重置为草稿,并且仅对(虚构的)群组 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 _notify_get_groups(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)._notify_get_groups(message, groups)
self.ensure_one()
if self.state == 'confirmed':
app_action = self._notify_get_action_link('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
请注意,我本可以在该方法之外定义求值函数,即定义一个全局函数来代替 lambda,但为了让这些有时略显乏味的文档文件更简洁、不那么冗长,我选择了前者而非后者。
覆盖默认值¶
有多种方式可以自定义 mail.thread 模型的行为,包括(但不限于):
_mail_post_access-Model属性在模型上发送消息所需的访问权限;默认需要
write权限,也可以设置为read- 上下文键:
这些上下文键可以在调用
create()或write()或其他任何可能有用的方法)时,在一定程度上控制mail.thread的特性,例如自动订阅或字段跟踪。mail_create_nosubscribe:在创建或 message_post 时,不将当前用户订阅到记录线程mail_create_nolog:在创建时,不记录自动生成的“<Document> 已创建”消息mail_notrack:在创建和写入时,不执行会生成消息的值跟踪tracking_disable:在创建和写入时,不执行任何 MailThread 功能(自动订阅、跟踪、发送消息等)mail_auto_delete:自动删除邮件通知;默认为 Truemail_notify_force_send:如果待发送的邮件通知少于 50 封,则直接发送而不使用队列;默认为 Truemail_notify_user_signature:在邮件通知中添加当前用户的签名;默认为 True
邮件别名¶
别名是可配置的电子邮件地址,与特定记录关联(该记录通常继承 mail.alias.mixin 模型),当通过电子邮件联系时会创建新记录。别名是让外部轻松访问系统的简便方式,允许用户或客户快速在数据库中创建记录,而无需直接连接 Odoo。
别名与收件邮件网关¶
有些人使用收件邮件网关来实现同样的目的。使用别名仍然需要正确配置的邮件网关,但单个 catchall 域名即可满足需求,因为所有路由都在 Odoo 内部完成。与邮件网关相比,别名具有以下优势:
- 更易于配置
单个收件网关可以被多个别名使用;这避免了在域名上配置多个邮箱(所有配置都在 Odoo 内部完成)
配置别名无需系统(System)访问权限
- 更连贯一致
在相关记录上配置,而非在设置的子菜单中
- 更易于在服务器端覆盖
混入模型从设计之初就便于扩展,使你能够比使用邮件网关更轻松地从收到的电子邮件中提取有用数据。
别名支持集成¶
别名通常配置在父模型上,通过电子邮件联系该模型时会创建特定记录。例如,项目(Project)拥有用于创建任务或问题的别名,销售团队(Sales Team)拥有用于生成线索(Lead)的别名。
Note
由别名创建的模型 必须 继承 mail_thread 模型。
别名支持通过继承 mail.alias.mixin 来添加;该混入会为父类中创建的每条记录生成一个新的 mail.alias 记录(例如,每条 project.project 记录在创建时都会初始化其 mail.alias 记录)。
Note
别名也可以手动创建,并通过一个简单的 Many2one 字段来支持。本指南假设你希望进行更完整的集成,包括自动创建别名、针对特定记录的默认值等。
与 mail.thread 继承不同,mail.alias.mixin 要求 进行一些特定的重写才能正常工作。这些重写将指定所创建别名的值,例如它必须创建的记录类型,以及这些记录可能根据父对象而具有的一些默认值:
- _get_alias_model_name(vals)¶
返回别名的模型名称。不是对现有记录回复的来信将导致创建该别名模型的新记录。该值可能取决于
vals,即创建该模型的记录时传递给create的值字典。- 参数
dict (vals) – values of the newly created record that will holding the alias
- 返回
model name
- 返回类型
str
- _get_alias_values()¶
返回用于创建别名的值,或在别名创建后写入别名的值。虽然并非完全强制,但通常需要通过在别名的
alias_defaults字段中设置默认值字典,来确保新建记录会关联到别名的父对象(例如任务在正确的项目中创建)。- 返回
dictionary of values that will be written to the new alias
- 返回类型
dict
_get_alias_values() 重写尤其有用,它让你能够轻松修改别名的行为。在可以设置到别名上的字段中,以下几项尤其值得关注:
alias_name-Char电子邮件别名的名称,例如如果想接收 <jobs@example.odoo.com> 的邮件则为 ‘jobs’
alias_user_id-Many2one(res.users)通过该别名收到邮件时创建的记录的所有者;如果未设置该字段,系统会尝试根据发件人(From)地址查找合适的所有者,如果该地址没有对应的系统用户,则使用管理员(Administrator)账户
alias_defaults-Text将在为此别名创建新记录时求值的 Python 字典,用于提供默认值
alias_force_thread_id-Integer可选的线程(记录)ID,所有来信都会附加到该记录,即使它们并非针对它的回复;如果设置了该字段,将完全禁用新记录的创建
alias_contact-Selection通过邮件网关在文档上发送消息的策略
everyone:所有人都可以发送
partners:仅限已认证的伙伴
followers:仅限相关文档的关注者或所关注频道的成员
请注意,别名使用了 委托继承,这意味着虽然别名存储在另一张表中,但你可以直接从父对象访问所有这些字段。这让你可以从记录的表单视图中轻松配置别名。
Example
让我们在商务差旅类上添加别名,以便通过电子邮件即时创建费用。
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
tracking=True)
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):
""" Specify the model that will get created when the alias receives a message """
return 'business.expense'
def _get_alias_values(self):
""" Specify some default values that will be set in the alias at its creation """
values = super(BusinessTrip, self)._get_alias_values()
# alias_defaults holds a dictionary that will be written
# to all records created by this alias
#
# in this case, we want all expense records sent to a trip alias
# to be linked to the corresponding business trip
values['alias_defaults'] = {'trip_id': self.id}
# we only want followers of the trip to be able to post expenses
# by default
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(),以便在创建费用时从电子邮件中获取值:
class BusinessExpense(models.Model):
# Previous code goes here
# ...
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 occurrence 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
活动跟踪¶
活动是用户需要对文档采取的操作,例如拨打电话或组织会议。活动随 mail 模块提供,因为它们集成在沟通栏中,但 不与 mail.thread 捆绑。活动是 mail.activity 类的记录,具有类型(mail.activity.type)、名称、描述、计划时间(等)等属性。待处理的活动显示在沟通栏部件中消息历史记录的上方。
你可以在对象上使用 mail.activity.mixin 类来集成活动,并在记录的表单视图和看板视图中使用特定部件来显示它们(通过字段 activity_ids)(分别是 mail_activity 和 kanban_activity 部件)。
Example
组织商务差旅是一个繁琐的过程,跟踪所需的活动(如订购机票或预约前往机场的出租车)可能会很有用。为此,我们将在模型上添加活动混入,并在差旅的消息历史记录中显示下一个计划活动。
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Business Trip'
name = fields.Char()
# [...]
我们修改差旅的表单视图以显示它们的下一个活动:
<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 -->
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
你可以在以下模型中找到集成的具体示例:
CRM(crm)应用中的
crm.lead销售(sale)应用中的
sale.order项目(project)应用中的
project.task
网站功能¶
访客跟踪¶
utm.mixin 类可用于通过指向指定资源的链接中的参数来跟踪在线营销/传播活动。该混入会为你的模型添加 3 个字段:
campaign_id:指向utm.campaign对象的Many2one字段(如 Christmas_Special、Fall_Collection 等)source_id:指向utm.source对象的Many2one字段(如搜索引擎、邮件列表等)medium_id:指向utm.medium对象的Many2one字段(如纸质邮件、电子邮件、社交媒体更新等)
这些模型只有一个字段 name 即它们只是用来区分活动,没有任何特定行为)。
一旦客户带着这些参数访问你的网站(即 https://www.odoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website),三个 cookie 就会在访客的网站上为这些参数设置。一旦从网站创建了继承 utm.mixin 的对象(如线索表单、职位申请等),utm.mixin 代码就会生效,从 cookie 中获取值并设置到新记录中。完成后,你就可以像使用其他字段一样,在定义报表和视图(分组等)时使用活动/来源/媒介字段。
要扩展此行为,只需向一个简单模型添加关系字段(该模型应支持 快速创建 (即以单个 name 值调用 create()),并扩展函数 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
这将告诉系统创建一个名为 odoo_utm_my_field 的 cookie,其值为 url 参数 my_field 中的值;一旦通过网站表单的调用创建了该模型的新记录,utm.mixin 的 create() 方法的通用重写就会从 cookie 中获取该字段的默认值(如果 my_module.my_track 记录尚不存在,则会即时创建)。
你可以在以下模型中找到集成的具体示例:
CRM(crm)应用中的
crm.lead招聘流程(hr_recruitment)应用中的
hr.applicant帮助台(helpdesk - 仅限 Odoo 企业版)应用中的
helpdesk.ticket
网站可见性¶
你可以非常轻松地为任何记录添加网站可见性开关。虽然这个混入手动实现相当容易,但它是继 mail.thread 继承之后最常用的;这证明了它的实用性。该混入的典型用例是任何拥有前端页面的对象;能够控制页面的可见性让你可以从容编辑页面,只在满意时才发布。
要包含该功能,你只需继承 website.published.mixin:
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.published.mixin']
这个混入会为你的模型添加 2 个字段:
website_published:表示发布状态的Boolean字段website_url:表示访问该对象的 URL 的Char字段
请注意,最后一个字段是计算字段,必须为你的类实现:
def _compute_website_url(self):
for blog_post in self:
blog_post.website_url = "/blog/%s" % (log_post.blog_id)
机制就绪后,你只需调整前端和后端视图使其可访问。在后端,通常的做法是在按钮盒(button box)中添加一个按钮:
<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="float-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 变量中指定你想在后端调用的动作的完整外部 id(注意该模型必须存在表单视图)。
动作 website_publish_button 定义在混入中,并会根据你的对象调整其行为:如果该类具有有效的 website_url 计算函数,用户点击按钮时将被重定向到前端;然后用户可以直接从前端发布页面。这确保了不会意外进行在线发布。如果没有计算函数,则只是简单地切换布尔值 website_published。
网站元数据¶
这个简单的混入只是让你能够轻松地在前端页面中注入元数据。
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.seo.metadata', 'website.published.mixin']
这个混入会为你的模型添加 3 个字段:
website_meta_title:允许你为页面设置附加标题的Char字段website_meta_description:包含页面简短描述的Char字段(有时用于搜索引擎结果)website_meta_keywords:包含一些关键词的Char字段,帮助搜索引擎更精确地对页面进行分类;“推广”工具将帮助你轻松选择词汇相关的关键词
这些字段可以在前端通过编辑器工具栏中的“推广”工具进行编辑。设置这些字段有助于搜索引擎更好地索引你的页面。请注意,搜索引擎的结果并非仅基于这些元数据;最佳 SEO 实践仍然是让可靠的来源引用你的内容。
其他¶
客户评分¶
评分混入允许发送电子邮件以征询客户评分、在看板流程中自动流转,并聚合评分统计数据。
为您的模型添加评分¶
要添加评分支持,只需继承 rating.mixin 模型:
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')
混入的行为会根据您的模型自动调整:
rating.rating记录将关联到您模型的partner_id字段(如该字段存在)。如果您使用的字段不是
partner_id,可以通过rating_get_partner_id()函数覆盖此行为
rating.rating记录将关联到您模型的user_id字段对应的合作伙伴(如该字段存在)(即被评分的合作伙伴)如果您使用的字段不是
user_id,可以通过rating_get_rated_partner_id()函数覆盖此行为(注意该函数必须返回一个res.partner,对于user_id系统会自动获取该用户的合作伙伴)
沟通栏历史中将显示评分事件(如果您的模型继承了
mail.thread)
通过电子邮件发送评分请求¶
如果您希望通过电子邮件请求评分,只需生成一封带有指向评分对象链接的电子邮件。一个非常基础的邮件模板大致如下:
<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="/rate/${access_token}/5">Satisfied</a></li>
<li><a href="/rate/${access_token}/3">Okay</a></li>
<li><a href="/rate/${access_token}/1">Dissatisfied</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>
请注意,评分有默认视图(kanban、pivot、graph),可让您快速总览客户评分情况。
你可以在以下模型中找到集成的具体示例:
项目(rating_project)应用中的
project.task帮助台(helpdesk - 仅限 Odoo 企业版)应用中的
helpdesk.ticket