Odoo 10 Learnodoo 10 中文文档odoo

odoo V10中文参考手册(十 : 有用的类和混入-mixin

2017-04-13  本文已影响1314人  XiaoHaiYang

有用的类和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.发布消息

参数:

  1. body (str) - 消息内容,一般是原始html,会自动进行过滤
  2. message_type (str) - 与mail_message.type字段对应
  3. content_subtype (str) - 如果是plaintext(纯文本),会被转成html
  4. parent_id (int) - 用于当在消息列表时以私信形式回复时使用,对应父级partner
  5. attachments (list(tuple(str,str))) - 使用(name,content)元组传送的附件列表,content是base64encode过的
  6. **kwargs - 其他的参数用于创建新mail.message记录

参数:
1.template_id - 用于渲染消息主体的模板id
2.**kwargs - 用于创建mail.compose.message(继承自mail.message) wizzard的参数

2.接收消息
在email在邮件网关中被处理时,下面方法会被调用,email可以是创建新的讨论(通过alias)或回复原来的讨论。覆盖他们可以使用邮件内容来设置对应讨论中的值(如更新时间或email地址等)

参数:
1.msg_dict (dict) - 一个包含email内容和附件的映射表,具体看:message_process, mail.message.parse
2.custom_values (dict) - 可选参数,附加的用于传递给记录创建的create()方法的字段,注意这里面的值可能会覆盖邮件里的内容。
返回:新创建的记录对象的id

参数:

  1. msg_dict (dict) - 一个包含email内容和附件的映射表,具体看:message_process,
  2. update_vals (dict) - 一个包含记录id和值的映射表用于更新,如果该参数为None或空,则不会进行更新操作

3.关注者管理

参数:
1.partner_ids (list(int)) - 当前记录对应的订阅的伙伴id列表
2.channel_ids (list(int)) - 订阅的渠道列表
3.subtype_ids (list(int)) - 所订阅的子类,默认None
4.force - 当值为true时会根据subtype来创建新关注关系(原关注关系会先删除)

返回:boolean 是否成功

参数:
1.user_ids (list(int)) - 当前记录对应的订阅的用户id列表,如果是None订阅当前用户
2.subtype_ids (list(int)) - 渠道或合作伙伴所订阅的子类id

参数:
1.partner_ids (list(int)) - 订阅该记录的伙伴id列表(官方是这样的,也许应该是取消订阅)
2.channel_ids (list(int)) - 订阅该记录的渠道列表(官方是这样的,也许应该是取消订阅)

参数:
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在没改变时也会跟着其他的信息改变显示

子类型

子类型可以对消息进行更细分的控制,给通知进行分类,允许订阅者决定接收哪些分类的通知。
子类型在模块中数据的形式创建,模型有以下字段:

可以通过覆盖_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方法来应用到不同的用户组中

参数:
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来生成

参数:
link_type (str) -- 生成的链接类型,可以是以下几种:

  1. view -- 链接到记录的表单视图
  2. assign -- 将当前登录用户赋值到记录的user_id字段(如果有该字段)
  3. follow ,unfollow -- 关注、取关
  4. workflow -- 触发一个工作流信号,工作流信号名需要通过kwarg 的signal传递
  5. method -- 基于记录调用方法,方法名需要通过kwarg的method传递
  6. 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.alias.mixin模型)的可配邮件地址,记录在通过邮件联系时创建,可以让外界更方便的接触系统,允许用户或客户快速新增记录而不需要直接访问odoo系统。

get_alias_values 覆盖方法让你可以很容易的修改别名的行为。有以下字段可以修改:

  1. alias_name - Char -- 邮件别名的名字,如想要获取jobs@example.odoo.com就给出jobs
  2. alias_user_id - Many2one (res.users) -- 当基于收到的邮件创建记录时,该记录的所有者,如果没有传的话系统会通过发件人邮箱来找用户,找不到的话就用管理员
  3. alias_defaults - Text -- 当为当前别名创建新记录时所使用的默认值,是一个python格式字典
  4. alias_force_thread_id - Integer -- 可选的与收到的消息相关联的线索ID,如果有设置就禁止创建新记录
  5. 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添加了三个字段:

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为模型添加两个字段:

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添加三个字段

这些字段可以通过编辑工具栏的推广工具修改。可以让搜索引擎更好的索引你的页面。

其他

客户评分

评分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添加以下功能

通过邮件发送评分请求

如果需要通过邮件来发送评分请求,只需要生成与相应对象关联的邮件即可。
简单的邮件模板如下:

<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,转载请注明出处

上一篇 下一篇

猜你喜欢

热点阅读