python开发odoo是什么_Odoo开发

Odoo开发入门课程的学生资料, 参考书点击这里.

概述

利用继承机制, Odoo可以在不直接修改底层对象的情况下为应用增加特性, 不需要修改已存在的模块, 而是通过创建新的模块来实现对模块的修改. 继承可以应用在所有的级别, 例如模型, 视图, 业务逻辑.

本文通过继承机制为todo_app增加社交和消息特性.

用户共享特性路线图为Task模型增加字段, 例如任务的责任人;

修改业务逻辑, 用户只能操作自己负责的任务, 而不是所有能看到的任务;

视图添加显示的字段;

添加社交网络特性: 消息墙以及关注.

创建模块框架

创建一个名为todo_user的odoo模块, 创建__init__.py文件以及__openerp__.py, 修改__openerp__.py文件内容为:

{

'name': 'Multiuser To-Do',

'description': 'Extend the To-Do app to multiuser.',

'author': 'Jeff Zhang',

'depends': ['todo_app'],

}

设置->更新模块列表->本地模块查找”todo_user”, 安装该模块.

模型扩展

扩展To-Do的模型的说明

Odoo中实体模型使用Python类来定义, 扩展模型时也是使用Python类, 但是需要使用Odoo的一个特殊机制.

为了扩展一个已有的模型, 需要使用一个具有_inherit属性的Python类来定义. _inherit属性指明扩展/继承哪个模型. 新的模型继承了父模型的所有特性, 在新类中定义我们期望的特性即可.

在Odoo中, 模型是独立于特定模块的, 可以通过self.env[]获得模型的引用. 例如模型res.partner的引用可以使用self.env[‘res.partner’]获得.

当服务启动后模块载入时, 任何扩展的模型修改只会影响之前载入的模块, 所以载入顺序很重要需要确认模块依赖关系是正确的.

向模型增加一个字段

扩展todo.task模型, 增加以下字段: 任务的责任人, 最后期限日期.

创建models子目录, 并在其中创建todo_task.py文件, 内容如下:

# -*- coding: utf-8 -*-

#!/usr/bin/env python

from openerp import models, fields, api

class TodoTask(models.Model):

_inherit = 'todo.task'

user_id = fields.Many2one('res.users', 'Responsible')

date_deadline = fields.Date('Deadline')

注意_inherit属性, 此处其值为’todo.task’, 说明这个类继承自todo.task模型. 使用了_inherit属性, 那么_name属性不需要了.

在models下创建__init__.py文件, 内容如下:

# -*- coding: utf-8 -*-

#!/usr/bin/env python

import todo_task

在模块根目录下的__init__.py中增加:

from models import todo_task

更新模块, 在技术特性中查看模型, 可以看到模型todo.task中增加了的新字段.

67009539_1.png

修改已存在的字段

除了通过继承增加新的字段, 自odoo8.0开始, 还可以修改已存在的字段的属性, 只需简单地添加相同名称的字段并设置字段属性值即可.

例如, 为了改变name字段的help tooltip, 在todo_task.py中增加一句即可:

name = fields.Char(help="What needs to be done?")

升级模块, 查看模型, 可以发现修改后的变化.

修改模型的方法

继承还可以应用在业务逻辑上, 例如向模型添加新的方法, 简单地在派生类中定义相应的函数即可.

覆盖已有的业务逻辑

如果希望扩展已存在的业务逻辑, 可以定义一个同名的方法覆盖已有模型中的方法, 并且在这个派生类的方法中还可以使用Python的super关键字来访问其覆盖的父方法.

示例: 覆盖To-Do应用模型中的do_clean_done方法, 该方法之前的业务逻辑是将所有is_done为True的记录的active字段赋值为False, 原来的代码如下:

@api.multi

def do_clear_done(self):

done_recs = self.search([('is_done', '=', True)])

done_recs.write({'active': False})

return True

由于现在希望每个用户只能操作他自己的任务, 所以需要修改do_clean_done的业务逻辑, 修改后的代码如下:

@api.multi

def do_clear_done(self):

domain = [('is_done', '=', True),

'|', ('user_id', '=', self.env.uid),

('user_id', '=', False)]

done_recs = self.search(domain)

done_recs.write({'active': False})

return True

发生的修改很简单, 就是将之前的记录过滤条件由仅仅考虑is_done为True改为is_done为True并且user_id为当前用户或user_id为空(False).

odoo中过滤条件使用一个domain来描述, 一个domain用包含多个条件的列表来描述. 每个条件用一个元组来表示, 各个条件之间为’与’的关系. 如果希望表达’或’的关系, 先使用’|’, 在’|’后面的两个元组为’或’关系的两个条件表达式.

这样, 新的方法覆盖了父方法. 更新并升级模块, 还看不出效果, 因为现在所有任务的user_id还都是False.

改进已有的业务逻辑

除了上例中完整的替换某个业务逻辑外, 更多的情况是在不破坏已有业务逻辑并利用它的情况下扩展它, 比如增加预处理或后处理等. 下面的代码在现有的标记任务完成的业务逻辑基础上, 增加一个用户身份的判断:

@api.one

def do_toggle_done(self):

if self.user_id != self.env.user:

raise Exception('Only the responsible can do this!')

else:

return super(TodoTask, self).do_toggle_done()

使用Python的super()方法可以调用父类中的方法, super相关信息如下:

Help on class super in module __builtin__:

class super(object)

| super(type) -> unbound super object

| super(type, obj) -> bound super object; requires isinstance(obj, type)

| super(type, type2) -> bound super object; requires issubclass(type2, type)

| Typical use to call a cooperative superclass method:

| class C(B):

| def meth(self, arg):

| super(C, self).meth(arg)

视图扩展

基本视图扩展

在odoo中, 视图继承的形式如下:

Todo Task form – User extension

todo.task

inherit_id域指定欲扩展的视图, 它的ref属性用来指定欲扩展视图的外部ID.

外部ID跨模块地唯一标识一个资源, 更多的信息在后续的内容中介绍.

Form, Tree/List以及Search视图都是使用arch的XML结构来定义的, 为了修改这个XML, 需要定位XML元素. 在XML中定位元素最直接的方法是使用XPath表达式, 例如这个元素可以使用下面的表达式来定位:

//field[@name]='is_done'

在此基础上, 如果希望在is_done之前增加date_deadline字段, 可以使用下面的XML实现:

除了XPath外, odoo还提供了更加方便的标记方法, 可以避免使用XPath, 上面的XML可以用简化为下面的内容:

用于确定新字段显示位置的定位, 除此之外, , ,

等都有类似特性.

相应地, name属性是经常用到的资源查找方式, 但是也可以使用其他的方式, 例如如果需要查找显示某些特定字符串的label或者CSS类名, 就可以使用string.

position属性用于指定新增元素的位置, 可以有以下的选择:

1.after: 在匹配元素之后, 与匹配元素具有相同的父元素;

2.befor: 在匹配元素之前, 与匹配元素具有相同的父元素;

3.inside(默认值): 作为匹配元素内容;

4.replace: 替换匹配元素的内容, 如果值为空将会删除匹配的元素;

5.attributes: 修改匹配元素的属性.

position属性的这几种可选值大部分容易理解, 只对attributes进行说明. 如果position属性设定为attributes, 那么需要使用value设定该属性的新值. 例如下面的代码将invisible属性设置为1:

1

新建一个views文件夹, 在其中新增一个todo_view.xml文件, 将基本视图扩展代码写入其中:

Todo Task form – User extension

todo.task

I have to...

最后, 在__openerp__.py中增加data描述:

'data': ['views/todo_view.xml'],

更新模块, 创建新任务, 发现新增特性已经应用了, 如下图.

67009539_2.png

新增一个任务, 然后设定为完成, 切换到管理员角色可以看到包括已完成任务在内的所有任务, 但是现在点击清除已完成任务, 不会将非admin用户的任务的activity设置为False, 满足预期的业务逻辑要求.

但现在存在一个问题: 如果使用admin用户创建一个任务, 并将Responsible设定为另一个用户(例如aaa), 以aaa登录并不能看到这个由admin分配的任务. 这是由于之前设定了行级访问规则, 其中的下面这一句设定导致不能显示, 这个问题在本文最后一部分解决.

[('create_uid','=',user.id)]

List/Tree视图和搜索视图扩展

向todo_view.xml中增加下面的内容扩展List/Tree视图:

Todo Task tree – User extension

todo.task

向todo_view.xml中增加下面的内容扩展搜索视图:

Todo Task tree – User extension

todo.task

domain="[('user_id','in',[uid,False])]" />

domain="[('user_id','=',False)]" />

更新模块, 看效果.

67009539_3.png

关于扩展模型的更多讨论

前面介绍的模型继承是最基本的模型继承方式, 但是还有更强力的混合类继承和代理继承方式. 前者通过继承将某个模型的所有功能特性加入另一个模型中, 后者通过一种透明的方式访问另一个模型中的数据.

利用原型继承实现模型特性的拷贝

之前使用_inherit属性来扩展一个模型, 定义一个继承todo.task模型的类, 然后向该其添加了一些特性, 这种方法中类的_name属性没有进行设定, 仍然是todo.task.

如果使用模型的_name属性, 可以创建混合类(mixin class).

from openerp import models

class TodoTask(models.Model):

_name = 'todo.task'

_inherit = 'mail.thread'

以上代码将模型mail.thread的特性拷贝到模型todo.task, 使todo.task具有了mail.thread的功能特性. mail.thread实现了消息和关注功能, 这个功能是可重用的, 所以可以非常方便地将该特性添加到其他模型上.

上面提到的拷贝意味着继承的新模型中可以获得被继承的所有字段和方法, 也即被继承模型中的字段也将在继承的新模型对应的数据库表中创建, 并且新模型中也包括未继承前的所有字段. 旧模型和继承后的新模型的数据被分别保存, 不会产生冲突. 下图是一个示意图.

67009539_4.png

上图中最上方可以看到一个名为”gogogo”的任务中, 用户aaa发送了两条消息给所有关注该任务的用户. 在数据库的todo_task表(旧模型)中, 只保存旧的字段, 而社交网络数据则保存在mail_message表(新模型中包括)中并与todo_task中的记录关联起来.

混合模型一般用于抽象模型, 例如mail.thread. 抽象模型与通常的模型类似, 只是抽象模型不在数据库中创建相应的数据表, 所以抽象模型更像是一个模板, 描述重用于普通模型时的字段和业务逻辑, 抽象模型中定义的字段只会在继承自它的普通模型中创建.

一般使用混合(mixin)继承时, 很少继承自普通模型, 因为这种继承机制将会复制相同的数据结构. 如果必须继承普通模型, 那么应该使用odoo提供你的代理继承机制, 这种机制能够避免数据结构的复制.

使用代理继承嵌入模型

代理继承使用_inherits属性(多一个s)将继承的模型映射到字段.

from openerp import models, fields

class User(models.Model):

_name = 'res.users'

_inherits = {'res.partner': 'partner_id'}

partner_id = fields.Many2one('res.partner')

例如以上的代码是标准的Users模型(res.users), 其中嵌入了了一个Partner模型. 当创建一个新的User时, 一个Partner将会同时创建, 并且partner_id中将会保存这个Partner的引用, 这种机制类似OO中的多态概念.

嵌入的模型(Partner)中的所有字段就像是User的字段一样, 可以以相同的形式直接使用. 例如, partner的name和address字段使用起来与使用User本身的字段一样, 只不过它们的数据实际上保存在关联的Partner模型中. 这样没有数据结构的复制.

注意, 代理继承中只有字段被继承, 方法是不能继承的!

为TODO应用增加社交网络功能

基本步骤

社交网络模块(技术名称mail)提供的消息板可以在很多页面的底部看到, 也被称作Open Chatter.

社交网络消息功能通过mail模块的mail.thread模型提供, 这是一个抽象模型, 可以用以下步骤将其添加到其他模型:

Step 1. 模块中添加对mail模块的依赖;

Step 2. 模型集成于mail.thread模型;

Step 3. 向该模型的form视图中添加Followers和Thread widgets;

Step 4. 为followers设定行级访问规则(可选).

Step1 to Step3

Step1, todo_task依赖于todo_app, 而todo_app依赖于mail, 所以不需要再做;

Step2, 之前我们的模型已经使用_inherit继承了todo.task模型, 因为odoo的_inherit可以接受多个模型继承, 使用列表即可, 所以对todo_task.py进行如下修改:

由:

_inherit = 'todo.task'

改为:

_name = 'todo.task'

_inherit = ['todo.task', 'mail.thread']注意: 创建抽象模型: 派生自models.AbstractModel而不是models.Model即可.

Step3, 使用视图继承将社交网络小组件(widgets)摆放在form底部, 打开todo_view.xml, 添加下面的内容到view_form_todo_task_inherited的arch部分:

message_follower_ids和message_ids这两个字段在mail.thread模型中定义.

更新模块, 看效果. 关注一个任务, 然后登录其他用户打开这个任务, 在下方的社交组件处向关注这个任务的用户发送消息, 其他所有关注该任务的用户都可以收到这条消息了.

67009539_5.png

修改菜单项

与视图不同, 一般数据记录没有XML的arch结构, 所以不能通过XPath表达式扩展和修改. 想要修改这些数据, 需要使用另一种方式.

利用可以完成模型上的插入或更新操作: 如果x不存在, 插入记录; 否则, 对其进行更新.

例如下面的代码修改菜单项, 将其添加到todo_user模块的todo_view.xml中:

My To-Do

id="todo_app.action_todo_task">

{'search_default_filter_my_tasks': True}

67009539_6.png

Step4, 修改行级访问规则

在之前的To-Do引用中, 应用了一条行级访问规则, 使得任务只对创建它的用户可见, 但是现在增建了社交网络, 用户之间可以加入协作, 任务需要对其他的用户可见, 例如被分配为任务责任人的让用户或关注任务的用户.

这里需要做的工作于上一节修改菜单项类似: 重写todo_app.todo_task_user_rule, 修改domain_force字段为新的值(现在是任务创建用户ID与当前用户相同). 但是由于todo_app中设定了, 所以不能进行写操作, 这里需要做的是: 删除之前的行级访问规则并且创建一个新的.

为了保持模块组织性, 新建一个security子目录并在其中创建一个名为todo_access_rules.xml的文件, 内容如下:

ToDo Tasks only for owner

eval="[(4, ref('base.group_user'))]"/>

['|',('user_id','in', [user.id,False]),

('message_follower_ids','in',[user.partner_id.id])]

以上代码首先删除已有的todo_task_user_rule记录, 然后创建一条新的记录.需要注意的是对于Followers的处理, 因为followers是partners, 并不是User对象, 所以需要使用user.partner_id.id而不是user.id.

还可以在开发时将改为, 正式发布时再改回来.

最后, 修改__openerp__.py中的data属性:

'data': [

'views/todo_view.xml',

'security/todo_access_rules.xml',

],

更新模块, 看效果吧, 之前提到的由于行级访问控制规则引起的现象没有了, 一切都如计划运行.

67009539_7.png

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值