说明
- Odoo在版本变更时,代码会出现一些变更,如果不注意,在新版本中使用旧的方式进行开发,就会出现问题。本篇文章结合OCA社区的版本迁移指南,对版本变更时,在开发中需要注意的问题进行说明。
- 注:本篇文章没有完全翻译OCA社区的迁移指南,只对开发时需要注意的地方进行了说明,如果需要进行版本迁移,请参考OCA社区的版本迁移指南
Odoo11 --> Odoo12
一、Date/Datetime字段的变更
- 在Odoo11以及之前的版本中,
fileds.Date/Datetime字段的值在运行时以字符串的形式存在,因此在做一些时间处理时需要先通过fields.Date/Datetime.from_string方法将字段的值转换为时间对象,然后再进行处理。 - 在Odoo12中,
fileds.Date/Datetime字段的值在运行时以时间对象的形式存在,在做时间处理时可以直接使用字段的值进行操作,而无需做转换。- 注:
fields.Date/Datetime.from_string方法在Odoo12中已经移除。如果需要使用Odoo提供的方法将时间字符串转换为时间对象,可以使用Odoo12中新增的方法fileds.Date.to_date/fields.Datetime.to_datetime
- 注:
二、新增装饰器@api.model_create_multi
- Odoo11中模型的create方法一次只能创建一个记录,创建多个记录时需要循环创建。
- Odoo12对create方法进行了性能优化,现在既可以创建单个记录也可以创建多个记录。
- 在开发中尽量使用ORM提供的方法操作数据库,Odoo会持续的对这些方法的底层操作进行优化
- 在Odoo12中对模型的create方法进行复写(overwrite)时,需要为方法添加
@api.model_create_multi装饰器。
三、属性_parent_store的变更
- 在Odoo12中,模型如果使用了
_parent_store = True属性,现在无需再添加parent_left/parent_right字段,只需添加parent_path = fields.Char(index=True)字段即可。使用参考
四、标签<label>的变更
- 在视图中使用
<label>标签时,必须要添加for=""属性
五、标签<filter>的变更
- 在Search视图中使用
<filter>标签时,必须添加name属性
六、标签<button>的变更
- 在Tree视图中使用
<button>标签时,应当添加string属性,来增加代码的可读性
七、SASS的变更
- 在Odoo12中使用SCSS来组织样式
八、关联(related)字段的变更
- 在Odoo12中关联字段默认是只读的,无需再为字段添加
readonly=True属性
Odoo12 --> Odoo13
一、装饰器的变更
- 在Odoo13中,模型的方法无需再添加装饰器
@api.multi,@api.returns,@api.one,@api.cr,@api.model_cr,所有方法默认都是@api.multi装饰的。 - 新的代码要适配
@api.multi装饰器。例如之前使用@api.one装饰的方法,方法的self参数代表模型的单条记录,在Odoo13中模型的方法默认使用@api.multi装饰器,self代表记录集,要操作记录集的单条记录,只能通过遍历self,获取单条记录后,再进行操作。
二、计算字段的变更
- 非存储型的计算字段,计算完成后不能出现空值。为防止出错,可以在计算方法的开头处添加代码
self.<field> = ...,为所有的记录添加一个该计算字段的默认值。 - 存储型的计算字段,如果计算后没有为字段赋值(没有满足条件),字段将会继续使用之前的值,因此存储型的计算字段在定义时,要有一个默认值,可以使用
default属性在定义字段时设置一个默认值。
三、多公司记录规则的变更
- 多公司的记录规则需要使用这种格式
['|',('company_id','=',False),('company_id','in',company_ids)]- 之前的多公司记录规则是这种格式
['|',('company_id','=',False),('company_id','=',user.company_id.id)]
- 之前的多公司记录规则是这种格式
- 使用这个规则后,在多公司环境下,用户无需切换公司,就可以访问其他公司(用户的
company_ids字段要包含该公司)的记录。如果使用不慎,可能出现混乱情况,例如用户既属于公司A又属于与公司B,用户在公司A中创建了一个销售订单,订单行中引入了公司B的产品,如果之后用户没有了公司B的权限,再次访问该订单就会出现错误。只有A公司访问权限的销售人员访问自己公司的销售订单也会报错。- 为了更好的使用多公司特性,可以阅读多公司指南
四、sudo方法的变更
- 现在不能通过
sudo(user)来切换用户,但是依然可以通过sudo()来切换到超级用户。 - 现在可以使用
with_user(user)来切换用户
五、track_visibility属性的变更
- 字段的
track_visibility='...'现在需要写成tracking=True
六、oldname属性的变更
- 字段的
oldname属性已经移除,不能在代码中使用该属性
七、ir.actions.act_window模型的变更
- 模型的
view_type字段已经移除,在定义窗口动作时,不能再定义view_type。 - 模型的
multi字段已经移除,现在如果想要为列表视图的添加一个动作,需要使用<field name="binding_view_types">list</field>。如果使用<act_window>定义窗口动作,则使用binding_views属性。参考示例:为列表视图中选中的客户群发短信
八、归档方式的变更
- 在Odoo13之前的版本中,模型如果添加了
active字段,在视图定义中可以为该字段添加toggle_button/boolean_button部件(Widget)来方便用户快速对记录进行归档。在Odoo13中,使用了新的方式来对归档记录进行标记,已归档的记录会在表单的右上角会出现一个标记,如下图:
-
使用示例:
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/> <field name="active" invisible="1" /> -
表单视图添加
active字段后,在动作下列菜单中会多出一个归档按钮,可以对记录进行归档/取消归档操作
九、Float字段的精度取值方式变更
- Odoo13之前的版本中,如果要为
Float字段指定精度,需要通过get_precision方法获取定义的精度,例如:x = fields.Float(digits=dp.get_precision("Account")) - 在Odoo13中,对精度的取值进行了简化,无需使用方法即可获取指定的精度,例如:
x = fields.Float(digits="Account")
十、external_dependencies键对应值的变更
- 如果清单(
manifest)文件中使用了external_dependencies这个键,需要使用Python的包名来作为该键的值,Odoo13之前的版本使用该键时,使用包的导入名称来作为键的值。参考- 使用
pip进行安装包时使用的是Python的包名,在代码中使用import导入时,使用的是导入名称。
- 使用
十一、新增表达式获取当前激活公司
- 在Odoo13中使用
.env.company来获取用户当前激活的公司。Odoo13之前的版本中使用这个表达式.env.user.company_id来获取激活的公司
十二、人力资源模块的变更
- 在Odoo13中人力资源模块引入了两个新模型,
hr.employee.public与hr.employee.base。对员工的私人信息与公开信息进行了分离,增加了系统的稳定性与安全性。
hr.employee.public以SQL视图(View)的形式存储在数据库中,从hr.employee模型对应的表中进行取值。hr.employee.base是一个抽象模型(AbstractModel),定义了员工私有信息与公共信息模型共有的字段与方法
hr.employee模型的权限已经不对权限组base.group_user开放。- 在Odoo13中如果要为员工增加新的公开信息字段,应该继承修改
hr.employee.base模型。
十三、邮件模板的变更
- 在Odoo13中,邮件模板中不能再使用
format_tz方法,现在需要使用format_datetime方法来代替,使用该方法时需要指定时区与时间的格式 - 示例:
format_tz(object.date)现在需要写成format_datetime(object.date, tz=object.tz, dt_format="dd/MM/yyyy HH:mm")
十四、JavaScript的变更
- 使用Javascript原生的
Promise对象代替jQuery框架的promises,在Odoo13中$.when()需要写成Promise.resolve()。 - Odoo13之前的版本中,使用
config.debug来判断当前是否处于调试状态,在Odoo13中使用config.isDebug()来进行判断。
十五、关系字段的访问
Odoo13中记录集可以直接访问关系字段,效果等同于使用mapped方法
records.partner_id # == records.mapped('partner_id')
records.partner_id.bank_ids # == records.mapped('partner_id.bank_ids')
records.partner_id.mapped('name') # == records.mapped('partner_id.name')
Odoo13 --> Odoo14
一、Selection字段的变更
- 在Odoo14中为
Selection字段新增了selection_add属性,通过该属性可以为Selection字段增添新的值。 - 在使用该属性时需要同时定义
ondelete属性,来表明新增Selection值的模块被卸载时,对使用过该值的记录应该如何处理。ondelete属性接收四种值:callable:接收一个方法来处理相关记录set null:属性的默认值,Selection字段的值会设置为Falseset default:Selection字段的值会设置为字段定义的默认值cascade:模块卸载后,使用新增值的记录也会被删除
- 注意:与
Many2one字段的ondelete属性没有关系。Many2one字段的ondelete机制是通过PostgreSQL实现的。
- 示例:
selection_field = fields.Selection(selection_add=[("foo", "Foo")], ondelete={"foo": "set null"})
二、invisible与readonly属性变更
- 在XML中定义视图时,
invisible与readonly属性不再支持动态表达式,例如:state=='draft',在Odoo14中使用attrs属性来实现这些需求。 invisible与readonly属性现在依然支持从context中取值,例如:invisible="context.get('hide_location', False)"
三、移除快捷标签<act_window>与<report>
- 快捷标签
<act_window>与<report>在Odoo14中被移除,在XML中需要使用他们的完整定义<act_window>现在需要写成<record id="..." model="ir.actions.act_window">...</record><report>现在需要写成<record id="..." model="ir.actions.report">...</record>
- 标签改动的参考示例
四、<button>标签的变更
- 在Odoo14中,
list视图中的<button>标签会将string属性定义的文本作为按钮主体显示在视图中。 - 如果需要将文本作为提示框显示,则需要去掉
string属性,将需要提示的文本定义到title属性中。参考
五、瞬态模型的变更
- 在Odoo14中,瞬态模型(transient model)也需要添加安全规则
六、display_name字段的变更
- 如果需要复写
_compute_display_name方法来更改display_name字段的值,现在应该复写name_get方法,并且为name_get方法添加装饰器@api.depends来指定依赖字段。如果需要通过display_name字段来搜索记录,需要复写_name_search方法
七、_name_search方法的变更
_name_search方法现在的返回值是ids,之前的版本中该方法返回元组列表[(id, display_name), ...]
八、移除Char字段的size属性
Char字段的size属性不再生效
九、移除记录规则中对global字段的赋值
global字段是一个可存储的计算字段,字段的计算依赖于groups字段,因此不需要再对该字段进行赋值
十、新增with_company方法
- 在odoo14之前的版本中,使用
.with_context(force_company=...)方法来变更context中的公司,odoo14中替换为.with_company(...)方法
十一、移除ir.actions.*系列模型的公共访问权限
- 在odoo14中访问
ir.actions.*模型需要使用sudo权限,如果在代码中需要读取ir.actions.*模型的记录,可以使用_for_xml_id方法。- 注:
_for_xml_id方法调用了_xmlid_lookup方法,该方法通过SQL直接访问数据库,没有经过ORM系统
- 注:
- 代码详细变更参考
Odoo14 --> Odoo15
一、t-raw与t-esc指令的变更
- 使用
t-out代替t-raw,变更详情 t-esc指令也已经弃用,odoo15中t-esc指令只是t-out指令的别名,在实际使用中尽量使用t-out指令,方便未来版本的迁移
二、ir.model*模型访问权限的变更
- 移除权限组
base.group_user对ir.model*模型的访问权限 - 现在如果要在代码中访问这些模型,需要使用
sudo提权,或者使用系统提供的方法,例如_get、refself.env['ir.model'].search([('model', '=', 'res.partner')], limit=1).idself.env['ir.model']._get_id('res.partner')
三、邮件模板语法变更
- 邮件模板的语法变更为两部分,邮件模板的主体部分使用Qweb语法。subject, email_from, email_to这些字段使用
{{...}}代替${...} - 示例
四、资源文件声明位置变更
- 资源文件现在需要在
__manifest__.py文件中进行声明
"assets": {
'web.assets_qweb': [
'crm/static/src/xml/forecast_kanban.xml',
],
'web.assets_backend': [
'crm/static/src/js/crm_form.js',
'crm/static/src/js/crm_kanban.js',
'crm/static/src/js/forecast/*',
'crm/static/src/js/systray_activity_menu.js',
'crm/static/src/js/tours/crm.js',
'crm/static/src/scss/crm.scss',
'crm/static/src/scss/crm_team_member_views.scss',
],
},
五、Environment.manage()方法的变更
- 如果代码中需要使用
Environment()方法创建一个新的env,现在无需再添加Environment.manage()
六、新增装饰器api.ondelete
- 现在如果要根据某些条件限制记录的删除,无需再复写
unlink方法,只需添加一个使用了装饰器api.ondelete的方法即可。 - 如果模块卸载时,也需要对记录的删除进行限制,需要为装饰器添加
at_uninstall=True属性。 - 参考示例
Odoo15 --> Odoo16
一、新增类变量_rec_names_search
- 在Odoo16中如果要通过模型的指定字段搜索记录,无需再复写
name_search方法,只需将要搜索的字段添加到类变量_rec_names_search中即可,参考- 不支持非存储字段
- 如果需要按条件添加字段,可以使用
@property参考
二、视图(ir.ui.view)groups_id与groups的改动
改进groups
- Odoo16之前的版本中,定义视图时字段的
groups属性只是隐藏不能访问的信息,并没有完全移除信息。在Odoo16版本中,groups属性会完全移除不能访问信息参考 - 添加
groups属性的字段如果在视图的其他地方被使用,比如该字段在其他字段的attrs中被使用,就需要在视图定义中再次添加该字段,并且使用invisible=1属性将字段隐藏。 - 改动后受限字段是移除而不是隐藏,因此同一个字段,使用不同的组进行限制,可以定义多次,而不用担心定义被覆盖,可以实现之前版本无法实现的功能
<!-- 用户不属于group_hr_contract_manager权限组时,将无法打开部门信息,在创建记录时也无法创建部门记录 --> <field name="department_id" groups="!hr_contract.group_hr_contract_manager" options="{'no_open': True, 'no_create': True}"/> <field name="department_id" groups="hr_contract.group_hr_contract_manager"/>
移除groups_id
- 受益于
groups的改动,现在字段使用groups属性即可完全达到之前使用groups_id的效果(移除信息而不是隐藏),因此视图中不需要在使用groups_id
三、One2many字段在表单视图中的变更
One2many字段在表单中如果嵌套在group标签内,且使用了nolabel="1"属性,则字段不会自动展开填充表单的宽度,需要添加colspan="2"属性
四、弃用fields_get_keys方法
- 如果要获取模型的字段,使用模型的
_fields变量或者get_views()方法
五、弃用get_xml_id方法
- 使用
get_external_id方法替代
六、弃用flush与recompute方法
- 使用
flush_recordset、flush_model、env.flush_all方法代替 - 个人理解:这三个方法的引入可以方便开发者根据操作的粒度选用合适的方法,从而达到节省系统资源,高效处理用户请求的目的。
flush_recordset方法可以对记录集中的变动进行计算与提交flush_model方法可以对模型的所有记录集进行计算与提交env.flush_all方法可以对当前环境(env)中所有模型的变动记录进行计算与提交
七、弃用refresh与invalidate_cache方法
- 根据操作粒度不同,使用下列方法进行替代
invalidate_recordsetinvalidate_modelenv.invalidate_all
八、字段的index=属性现在可以选择更多的值
- 开发者可以根据字段类型以及字段值的存储情况选择更合适的索引算法,提高查询性能。详细参考
九、XML模板资源声明位置变更
- 在之前的版本中XML模板定义在
__manifest__.py文件的web.assets_qweb,这是一个伪资源包。在Odoo16中对前端(website)与后台(webclient)使用的XML模板进行了区分,后台使用的模板定义在web.assets_backend,前端(website)使用的模板定义在web.assets_frontend- 在Odoo中前端(frontend)指代门户网站(website),后端(backend)指代后台的管理系统(销售、库存...)
- 前端相关的代码(JS/XML/SCSS)在
web.assets_frontend中定义,后端代码在web.assets_frontend中定义
- 示例参考
十、字段添加unaccent=False来提升查询效率
- 为字段添加
unaccent=False后,对带音调的单词将不会进行区别,例如单词Hélène、Helene、hélène,在执行查询时将不会再进行区别。变更参考