混入类和实用类¶
Odoo 实现了一些有用的类和混入(mixins),使您能够轻松地在您的对象上添加常用行为。本指南将详细介绍其中大部分内容,并提供示例和使用场景。
消息功能¶
消息集成¶
基础消息系统¶
将消息功能集成到您的模型中非常简单。只需继承 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="business_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)用户)。如果您的邮件网关和收件箱地址配置正确,这些通知将通过电子邮件发送,并可以直接从您的邮件客户端进行回复;自动路由系统会将回复路由到正确的对话线程。
在服务器端,有一些辅助函数可以帮助您轻松地向记录发送消息并管理关注者:
发布消息
- message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, **kwargs)¶
在现有线程中发布新消息,并返回新的邮件.消息 ID。
- 参数
body (str | Markup) – 消息的内容。如果为
str
类型,将会被转义。对于 HTML 内容,请使用Markup
对象。message_type (str) – 请参见 mail_message.message_type 字段
parent_id (int) – 处理对之前消息的回复,如果为私有讨论,则将父级联系人添加到消息中
attachments (list(tuple(str,str))) – 附件元组列表,格式为
(name,content)
, 其中内容不是以 base64 编码的body_is_html (bool) – 表示
body
是否应被当作 HTML 处理,即使str
也是如此。**kwargs – 额外的关键字参数将用作新邮件.消息记录的默认列值
- 返回
新建的 mail.message 的 ID
- 返回类型
- message_post_with_view(views_or_xmlid, **kwargs):
辅助方法,使用 view_id 通过 ir.qweb 引擎进行渲染来发送邮件/发布消息。此方法是独立的,因为模板和邮件编辑器中没有功能可以批量处理视图。当模板能够处理 ir.ui 视图时,此方法可能会被移除。
- 参数
record (str or ir.ui.view) – 外部视图的 ID 或记录
- message_post_with_template(template_id, **kwargs)¶
用于通过模板发送邮件的辅助方法
- 参数
template_id – 消息正文所使用的模板的ID
**kwargs – 用于创建邮件撰写向导(继承自 mail.message)的参数
接收消息
这些方法在通过邮件网关处理新邮件时被调用。这些邮件可以是新线程(如果它们通过 别名 到达),也可以是现有线程的回复。重写这些方法允许你根据邮件本身的一些值来设置线程记录的值(例如,更新日期或电子邮件地址,将抄送地址添加为关注者等)。
- message_new(msg_dict, custom_values=None)¶
当为给定的线程模型接收到新消息时,由
message_process
调用,如果该消息不属于现有线程。默认行为是根据从消息中提取的一些基本信息,创建对应模型的新记录。可以通过重写此方法来实现额外的行为。
- message_update(msg_dict, update_vals=None)¶
在接收到现有对话线程的新消息时,由
message_process
调用。默认行为是使用来自传入邮件的update_vals
更新记录。可以通过重写此方法来实现额外的行为。
关注者管理
- message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)¶
将联系人添加到记录的关注者中。
- message_unsubscribe(partner_ids=None, channel_ids=None)¶
从记录的关注者中移除合作伙伴。
记录更改¶
mail
模块在字段上添加了一个强大的跟踪系统,允许您在记录的聊天中记录对特定字段的更改。要为字段添加跟踪功能,只需将跟踪属性设置为 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
字段也会在通知中显示,以提供更多关于该通知的上下文信息(即使名称未发生更改)。
子类型¶
子类型让您对消息拥有更细粒度的控制。子类型作为通知的分类系统,允许文档的订阅者自定义他们希望接收的通知类型。
子类型作为您模块中的数据进行创建;该模型具有以下字段:
名称
(必填) -Char
子类型名称,在通知自定义弹窗中显示
描述
-Char
用于在该子类型的消息中添加的描述。如果为空,则会使用名称代替
内部
-Boolean
带有内部子类型的消息仅对员工可见,即
base.group_user
用户组的成员。parent_id
-Many2one
链接的子类型用于自动订阅;例如,项目子类型通过此链接与任务子类型相关联。当某人订阅了一个项目,他将自动订阅该项目中所有通过父级子类型找到的任务子类型。
relation_field
-Char
例如,当关联项目和任务子类型时,关系字段是任务中的 project_id 字段。
res_model
-Char
模型,该子类型适用;如果为 False,则此子类型适用于所有模型
默认
-Boolean
是否在订阅时默认激活该子类型
序列
-Integer
用于在通知自定义弹窗中对子类型进行排序
隐藏
-Boolean
是否在通知自定义弹窗中隐藏该子类型
通过将子类型与字段跟踪相结合,可以根据用户可能感兴趣的不同类型的通知进行订阅。为此,您可以覆盖 _track_subtype()
函数:
- _track_subtype(init_values)¶
根据已更新的值,返回记录更改时触发的子类型。
- 参数
init_values (dict) – 记录的原始值;字典中仅包含已修改的字段
- 返回
一个子类型的完整外部 ID,如果没有触发子类型则为 False
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
字段从 草稿 更改为 已确认 时,我们希望使用我们新创建的子类型:
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)
自定义通知¶
在向关注者发送通知时,可以在模板中添加按钮,以便直接从电子邮件中快速执行操作,这会非常有用。即使是一个可以直接链接到记录表单视图的简单按钮也可能很有用;然而,在大多数情况下,你不希望将这些按钮显示给门户用户。
通知系统允许以下方式来自定义通知模板:
显示 访问按钮:这些按钮显示在通知邮件的顶部,允许收件人直接访问记录的表单视图
显示 关注按钮:这些按钮允许接收者直接快速地从记录中订阅
显示“取消关注按钮”:这些按钮允许接收者直接快速地从记录中取消关注
显示 自定义操作按钮:这些按钮是调用特定路由的按钮,允许您直接从邮件中执行一些有用的操作(例如将线索转换为商机、为费用管理员审核费用单等)。
这些按钮设置可以通过覆盖函数 _notify_get_groups
应用于您自己定义的不同用户组。
- _notify_get_groups(message, groups)¶
根据已更新的值,返回记录更改时触发的子类型。
- 参数
message (record) – 正在发送的
mail.message
记录groups (list(tuple)) – 包含以下形式的元组列表:(group_name, group_func, group_data),其中: group_name 是仅用于能够覆盖和操作组的标识符。默认组包括 ``user``(与员工用户关联的收件人)、``portal``(与门户用户关联的收件人)和 ``customer``(未与任何用户关联的收件人)。覆盖使用的示例是添加一个与 res.groups 如 Hr Officers 关联的组,以向他们设置特定的动作按钮。 group_func 是一个函数指针,接受一个合作伙伴记录作为参数。该方法将应用于收件人,以确定他们是否属于某个给定的组。只保留第一个匹配的组。评估顺序是列表中的顺序。 group_data 是一个包含通知邮件参数的字典,可能包含以下键值对: - has_button_access
- 返回
一个子类型的完整外部 ID,如果没有触发子类型则为 False
动作列表中的 URL 可以通过调用 _notify_get_action_link()
函数自动生成:
- _notify_get_action_link(self, link_type, **kwargs)¶
为当前记录(或如果设置了 kwargs 中的
model
和res_id
,则为特定记录)生成一个链接。
Example
让我们向差旅状态变更通知中添加一个自定义按钮;该按钮将把状态重置为草稿,并且仅对(虚构的)旅行管理员用户组(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: any(
user.sudo().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
- 模型属性该模型上发布消息所需的必填访问权限;默认情况下需要
write
访问权限,也可以设置为read
。- 上下文键:
这些上下文键可用于在调用
create()
或write()``(或任何其他可能有用的方法)时,对 ``mail.thread
功能进行一定程度的控制,例如自动订阅或字段跟踪。mail_create_nosubscribe
: 在创建或 message_post 时,不将当前用户订阅到记录的对话线程mail_create_nolog
:在创建时,不记录自动的“<文档> 已创建”消息mail_notrack
: 在创建和写入时,不执行值跟踪以生成消息tracking_disable
: 在创建和写入时,不执行 MailThread 功能(自动订阅、跟踪、发送消息等)mail_auto_delete
: 自动删除邮件通知;默认为 Truemail_notify_force_send
: 如果需要发送的邮件通知少于 50 条,则直接发送而不是使用队列;默认值为 Truemail_notify_user_signature
: 在邮件通知中添加当前用户的签名;默认为 True
邮件别名¶
别名是可配置的电子邮件地址,它们与特定记录(通常继承 mail.alias.mixin
模型)相关联,当通过邮件联系时会创建新记录。它们是一种让系统对外可用的简便方式,允许用户或客户快速在数据库中创建记录,而无需直接连接到 Odoo。
别名与传入邮件网关¶
有些人使用传入邮件网关来实现相同的目的。然而,您仍然需要正确配置的邮件网关才能使用别名,不过一个单一的通配域名就足够了,因为所有的路由将在 Odoo 内部完成。与邮件网关相比,别名具有许多优势:
- 更容易配置
一个传入网关可以被多个别名使用;这避免了在您的域名上配置多个电子邮件(所有配置都在 Odoo 内完成)
无需系统访问权限即可配置别名
- 更一致
可在关联记录中配置,而非在“设置”子菜单中
- 更易于在服务器端覆盖
混入模型从一开始就是为扩展而设计的,与邮件网关相比,它能更方便地从传入的电子邮件中提取有用数据。
别名支持集成¶
别名通常在父模型上配置,当通过邮件联系时,该模型将创建特定记录。例如,项目有别名用于创建任务或问题,销售团队有别名用于生成潜在客户。
注解
通过别名创建的模型**必须**继承 mail_thread
模型。
别名支持通过继承 mail.alias.mixin
实现;该混入类将在每个父类记录被创建时生成一个新的 mail.alias
记录(例如,每个 project.project
记录在创建时都会初始化其 mail.alias
记录)。
注解
别名也可以手动创建,并由一个简单的 Many2one
字段支持。本指南假设您希望实现更完整的集成,包括自动创建别名、记录特定的默认值等。
与 mail.thread
继承不同,mail.alias.mixin
需要 进行一些特定的覆盖以正确工作。这些覆盖将指定所创建别名的值,例如它必须创建的记录类型,以及可能根据父对象而定的一些默认值:
- _get_alias_model_name(vals)¶
返回别名的模型名称。未回复现有记录的传入邮件将导致创建此别名模型的新记录。该值可能取决于
vals
,即在创建此模型的记录时传递给create
的值字典。- 参数
dict (vals) – 新建记录的值,该记录将用于保存别名
- 返回
模型名称
- 返回类型
- _get_alias_values()¶
返回值用于创建别名,或在别名创建后对其进行写入。虽然并非完全必要,但通常需要设置一个默认值字典到别名的
alias_defaults
字段中,以确保新创建的记录将与别名的父级相关联(即任务将在正确的项目中创建)。- 返回
将要写入新别名的值的字典
- 返回类型
_get_alias_values()
的覆盖方法特别有用,因为它允许你轻松地修改别名的行为。在可以设置的别名字段中,以下字段尤其值得关注:
别名名称
-Char
邮件别名的名称,例如:如果你想接收 <jobs@example.odoo.com> 的邮件,可以设置为 “jobs”
alias_user_id
-Many2one
(res.users
)此别名接收到邮件时创建的记录的所有者;如果此字段未设置,系统将尝试根据发件人(From)地址查找正确的所有者,如果未找到对应的系统用户,则将使用管理员账户。
alias_defaults
-Text
用于在为该别名创建新记录时计算默认值的 Python 字典
alias_force_thread_id
-Integer
可选的线程(记录)ID,所有传入的消息都将附加到该线程上,即使它们并未对其进行回复;如果设置此选项,将完全禁用新记录的创建。
别名联系人
-Selection
用于通过邮件网关在文档上发布消息的策略
everyone: 每个人都可以发布
合作伙伴: 仅限已认证的合作伙伴
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
活动跟踪¶
活动是用户需要在文档上执行的操作,例如拨打电话或安排会议。活动由邮件模块提供,因为它们集成在聊天(Chatter)中,但*不与 mail.thread 捆绑*。活动是 mail.activity
类的记录,具有类型(mail.activity.type
)、名称、描述、计划时间(以及其他内容)。待处理的活动会在聊天小部件中的消息历史上方显示。
您可以使用 mail.activity.mixin
类将活动集成到您的对象中,并通过字段 activity_ids
在记录的表单视图和看板视图中显示它们(分别使用 mail_activity
和 kanban_activity
小部件)。
Example
组织一次商务旅行是一个繁琐的过程,跟踪所需的活动(如预订机票或机场出租车)可能会很有用。为此,我们将在我们的模型上添加活动混入(mixin),并在我们行程的消息历史记录中显示下一个计划的活动。
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Business Trip'
name = fields.Char()
# [...]
我们修改了行程的表单视图,以显示它们的下一个活动:
<record id="business_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.lead
在 CRM(crm)应用中销售订单
在销售(销售)应用中project.task
在项目(project)应用中
网站功能¶
访客跟踪¶
utm.mixin
类可用于通过链接到指定资源时的参数来跟踪在线营销/沟通活动。该混入类会向您的模型添加 3 个字段:
campaign_id
:Many2one
字段,指向utm.campaign
对象(例如 Christmas_Special、Fall_Collection 等)source_id
: 指向utm.source
对象的Many2one
字段(例如:搜索引擎、邮件列表等)medium_id
:Many2one
字段,指向utm.medium
对象(例如:平信、电子邮件、社交媒体更新等)。
这些模型只有一个字段 ``name``(即它们只是用来区分活动,但没有特定的行为)。
一旦客户通过这些参数访问您的网站(例如 https://www.odoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website),将在访客的网站上设置三个 cookie 来保存这些参数。当从网站创建一个继承 utm.mixin
的对象时(例如线索表单、职位申请等),utm.mixin
的代码会启动,并从 cookie 中获取值以设置到新记录中。完成此操作后,您就可以像使用其他字段一样,在定义报表和视图时使用 campaign/source/medium 字段(例如按组分类等)。
要扩展此行为,只需向一个简单模型添加一个关系字段(该模型应支持 *快速创建*(即调用 create()
并传入一个 name
值)),并扩展函数 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.lead
在 CRM(crm)应用中在招聘流程(hr_recruitment 应用)中的
hr.applicant
helpdesk.ticket
在帮助台(helpdesk - 仅限 Odoo 企业版)应用中
网站可见性¶
你可以非常轻松地在任何记录上添加一个网站可见性切换功能。虽然这个混入(mixin)手动实现起来相当简单,但它在 mail.thread
继承之后是最常被使用的;这证明了它的实用性。此混入的典型用例是任何具有前端页面的对象;能够控制页面的可见性,使你可以在编辑页面时有更多时间,仅在满意后才发布该页面。
要包含此功能,您只需继承 website.published.mixin
:
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.published.mixin']
此混入类在您的模型中添加了 2 个字段:
请注意,最后一个字段是一个计算字段,必须为您的类实现:
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="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
动作在 mixin 中定义,并根据您的对象调整其行为:如果该类具有有效的 website_url
计算函数,当用户点击按钮时,将被重定向到前端;用户随后可以直接从前端发布页面。这确保了不会意外发生在线发布。如果没有计算函数,则仅触发布尔值 website_published
。
网站元数据¶
这个简单的混入(mixin)允许你轻松地在前端页面中注入元数据。
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
字段,用于包含一些关键词,以帮助搜索引擎更精确地对您的页面进行分类;“推广”工具将帮助您轻松选择语义相关的关键词。
这些字段可以通过编辑器工具栏中的“提升”工具在前端进行编辑。设置这些字段有助于搜索引擎更好地索引您的页面。请注意,搜索引擎的结果不仅仅基于这些元数据;最佳的搜索引擎优化实践仍然是通过可靠的来源获得引用。
其他¶
客户评分¶
评分混入式功能允许发送邮件以请求客户评分,在看板流程中实现自动转换,并对您的评分进行统计汇总。
在您的模型上添加评分¶
要添加评分支持,只需继承 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
字段相关联。这种行为可以通过函数
rating_get_partner_id()
进行重写,如果您使用的是除partner_id
以外的其他字段。
如果您的模型中存在
user_id
字段,那么rating.rating
记录将与该字段的业务伙伴相关联(即被评分的业务伙伴)这种行为可以通过函数
rating_get_rated_partner_id()
进行重写,如果您使用的是除user_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>
请注意,评分功能默认提供了看板、透视表和图表视图,可让您快速了解客户的评分情况。
你可以在以下模型中找到集成的具体示例:
project.task
在项目(rating_project)应用中helpdesk.ticket
在帮助台(helpdesk - 仅限 Odoo 企业版)应用中