Odoo12自定义消息提示方案设计与实现

发布时间:2026/7/19 5:28:44
Odoo12自定义消息提示方案设计与实现 1. 项目概述在Odoo12开发过程中消息提示是一个高频需求场景。系统自带的异常提示机制虽然能用但存在两个明显痛点一是错误提示样式突兀显示为红色错误框二是在对话框场景下会导致表单数据丢失。这个痛点我在实际项目中遇到过多次特别是在销售订单、采购申请等业务单据的复杂校验场景中。传统方案直接使用raise UserError()会中断当前操作流程且提示框标题显示Odoo Server Error给终端用户造成不必要的恐慌。更严重的是当在动态生成的对话框表单中进行校验时这种粗暴的提示方式会导致已填写的表单数据全部清空——这意味着用户不得不重新填写所有字段。2. 核心方案设计2.1 技术选型分析Odoo平台本身提供了三种消息提示机制异常抛出raise UserError(message)样式固定为错误提示客户端通知self.env.user.notify_warning(message)但无法阻断流程向导模型(Wizard)通过TransientModel创建临时交互界面经过实际测试只有向导模型方案能同时满足以下需求可自定义提示样式去除错误标识保持当前表单数据不丢失支持用户确认交互兼容对话框场景2.2 实现架构拆解整个方案包含三个关键组件消息向导模型继承TransientModel的临时数据模型表单视图定义消息展示样式和确认按钮调用逻辑业务代码中的触发机制这种设计符合Odoo的模块化思想将消息提示功能解耦为独立组件可以在不同模块间复用。我在实际项目中将其封装为base_message模块被其他15个业务模块依赖。3. 详细实现步骤3.1 创建消息向导模型在模块目录下新建models/my_message_wizard.pyfrom odoo import models, fields, api class MyMessageWizard(models.TransientModel): _name my.message.wizard _description Custom Message Wizard message fields.Text( stringMessage Content, requiredTrue, readonlyTrue # 防止用户修改提示信息 ) api.multi def action_confirm(self): 确认按钮的响应方法 return {type: ir.actions.act_window_close}关键点说明必须使用TransientModel而非Model避免产生持久化数据message字段设为readonly确保提示信息不可篡改返回标准窗口关闭动作不执行其他业务逻辑3.2 设计消息视图在views/目录下创建my_message_wizard.xmlodoo record idmy_message_wizard_form modelir.ui.view field namenamemy.message.wizard.form/field field namemodelmy.message.wizard/field field namearch typexml form stringSystem Notification sheet div classoe_title h1i classfa fa-info-circle/ Notification/h1 /div group field namemessage widgethtml classoe_read_only/ /group /sheet footer button nameaction_confirm stringConfirm typeobject classbtn-primary default_focus1/ /footer /form /field /record /odoo视图优化技巧使用sheet布局保持样式统一添加Font Awesome图标增强视觉效果采用html widget支持富文本消息设置default_focus让按钮自动获得焦点3.3 注册模块资源在__manifest__.py中添加声明{ name: Custom Message Wizard, version: 12.0.1.0.0, depends: [base], data: [ views/my_message_wizard.xml, ], installable: True, }4. 业务集成方案4.1 基础调用示例在任意模型方法中触发提示def button_validate(self): if not self.valid_condition: message self.env[my.message.wizard].create({ message: pThis order cannot be validated because:/p ul liMissing required fields/li liInventory not available/li /ul }) return { name: Validation Error, type: ir.actions.act_window, view_mode: form, res_model: my.message.wizard, res_id: message.id, target: new, context: {create: False} # 禁止新建 } return super().button_validate()4.2 高级应用场景场景1表单动态校验api.onchange(partner_id) def _onchange_partner(self): if self.partner_id.blacklisted: return { warning: { title: Blacklist Alert, message: This customer is in blacklist!, } } # 替换为 if self.partner_id.blacklisted: message self.env[my.message.wizard].create({ message: Customer is in blacklist! }) return { type: ir.actions.act_window, res_model: my.message.wizard, res_id: message.id, views: [(False, form)], target: new }场景2批量操作提示def action_batch_confirm(self): invalid_records self.filtered(lambda r: not r.valid) if invalid_records: messages [] for rec in invalid_records: messages.append( fli{rec.name}: Missing {rec.missing_field}/li ) message self.env[my.message.wizard].create({ message: f pCannot process these records:/p ul{.join(messages)}/ul }) return { name: Batch Error, type: ir.actions.act_window, res_model: my.message.wizard, res_id: message.id, target: new } return super().action_batch_confirm()5. 样式优化技巧5.1 CSS自定义在static/src/css/message.css中添加.o_my_message_wizard { max-width: 600px !important; } .o_my_message_wizard .oe_title h1 { color: #875A7B; } .o_my_message_wizard .message_content { padding: 15px; background: #f8f9fa; border-radius: 3px; }然后在XML视图中引入template idassets_backend inherit_idweb.assets_backend xpath expr. positioninside link relstylesheet href/module_name/static/src/css/message.css/ /xpath /template5.2 响应式布局优化field namemessage classoe_read_only div classrow mt16 div classcol-md-12 message_content t t-escmessage/ /div /div /field6. 常见问题排查6.1 消息框不显示可能原因及解决方案模块未正确安装检查__manifest__.py中的文件路径更新模块列表odoo-bin -u module_name权限问题_name my.message.wizard _inherit [mail.thread] _description Custom Message Wizard视图加载失败检查XML文件语法python -m xmllint my_message_wizard.xml查看服务端日志中的视图加载错误6.2 表单数据丢失典型场景在对话框表单中进行校验时调用传统UserError会导致表单重置解决方案确保使用Wizard方案而非raise UserError在返回动作中添加context: {create: False}检查是否误用了default_get方法覆盖字段值6.3 多语言支持实现方案在i18n/目录下添加翻译文件消息内容使用_()函数包裹message _(Validation failed for these reasons:)视图中的字符串也需添加翻译标记button nameaction_confirm stringConfirm .../ 改为 button nameaction_confirm string_(Confirm) .../7. 性能优化建议批量消息处理 当需要显示多条消息时应该合并为单次调用# 反模式 - 多次创建wizard for error in errors: self.env[my.message.wizard].create({message: error}) # 推荐方案 - 合并显示 full_message br.join(errors) self.env[my.message.wizard].create({message: full_message})缓存消息模板 对于频繁使用的消息可以预定义模板class MyMessageWizard(models.TransientModel): _inherit my.message.wizard api.model def get_template(self, template_name): templates { invalid_date: _(Date cannot be in past), missing_required: _(Required fields are missing) } return templates.get(template_name, )异步消息显示 对于耗时操作可以使用延迟消息def long_running_method(self): # 立即返回响应 message self.env[my.message.wizard].create({ message: Processing started... }) self.env.cr.commit() # 后台处理 def _background_work(): try: # 实际业务逻辑 self._do_heavy_work() # 完成后通知 self.env.user.notify_success(Processing completed) except Exception as e: self.env.user.notify_danger(str(e)) # 触发异步任务 threading.Thread(target_background_work).start() return { type: ir.actions.act_window, res_model: my.message.wizard, res_id: message.id, target: new }