什么是 default_get?
想象一下你走进一家智能咖啡店:
- 店员(Odoo系统)知道你昨天点了拿铁
- 今天你刚进门,店员就问:"还是老样子,一杯中杯拿铁吗?"
- 你只需说"是"或进行少量调整
default_get 就是这个智能店员 ------它在你创建新记录时,自动提供合理的默认值,让你只需关注需要修改的部分。
default_get 的核心功能
1. 智能预填充字段值
default_get 的主要工作是:在用户创建新记录时,自动填充合理的初始值,而不是让所有字段都为空。
@api.model
def default_get(self, fields):
# ...处理逻辑...
return defaults # 返回包含默认值的字典
2. 与普通默认值的区别
|------------------------|----------|---------|-----------|
| 默认值类型 | 设置位置 | 灵活性 | 依赖上下文 |
| 字段定义中的 default | 模型字段定义 | 低 | ❌ 不依赖 |
| _defaults****属性 | 模型类属性 | 中 | ❌ 不依赖 |
| default_get****方法 | 模型方法 | 高 | ✅ 依赖 |
-
字段 default:只能设置简单值或无参函数
name = fields.Char(default="New Meeting")
-
default_get:可以基于完整上下文提供智能默认值
如果从联系人页面创建,自动将该联系人设为参会者
if context.get('active_model') == 'res.partner':
defaults['partner_ids'] = [(4, context.get('active_id'))]
在日历事件中的实际应用
查看提供的代码:
@api.model
def default_get(self, fields):
# 处理 res_model 和 res_model_id 的相互转换
context = dict(self.env.context)
if context.get('default_res_model') and not context.get('default_res_model_id'):
context.update(
default_res_model_id=self.env['ir.model']._get_id(context['default_res_model'])
)
if context.get('default_res_model_id') and not context.get('default_res_model'):
context.update(
default_res_model=self.env['ir.model'].browse(self.env.context['default_res_model_id']).sudo().model
)
# 调用父类的 default_get 获取基础默认值
defaults = super(CalendarEvent, self.with_context(context)).default_get(fields)
# 处理从其他模型创建日历事件的场景
if 'res_model_id' not in defaults and 'res_model_id' in fields and \
context.get('active_model') and context['active_model'] != 'calendar.event':
defaults['res_model_id'] = self.env['ir.model']._get_id(context['active_model'])
defaults['res_model'] = context.get('active_model')
if 'res_id' not in defaults and 'res_id' in fields and \
defaults.get('res_model_id') and context.get('active_id'):
defaults['res_id'] = context['active_id']
return defaults
通俗解释这段代码
想象你正在从客户联系人页面创建会议:
- 上下文准备阶段 :
- 你点击联系人 John Doe 页面上的"安排会议"按钮
- 系统自动设置上下文:
{'active_model': 'res.partner', 'active_id': john_doe_id}
- default_get****工作流程 :
- 检查是否有模型名称但没有模型ID → 自动转换
- 检查是否有模型ID但没有模型名称 → 自动转换
- 调用父类获取基础默认值(如当前用户作为组织者)
- 发现是从联系人页面创建 → 自动关联到 John Doe
- 返回包含这些智能默认值的字典
- 结果 :
- 会议表单自动打开
- "组织者"已设为你自己
- "参会者"已包含 John Doe
- 无需手动选择联系人
default_get 的工作原理
1. 调用时机
default_get 在以下情况被调用:
- ✅ 用户点击"创建"按钮时
- ✅ 从其他记录打开"创建"向导时(如从销售订单创建发票)
- ✅ 通过代码调用
create()但未提供某些字段值时 - ✅ 通过API创建新记录时
2. 方法签名
@api.model
def default_get(self, fields):
# self: 空记录集(因为是创建新记录)
# fields: 需要获取默认值的字段列表
# 返回: 包含字段名和默认值的字典
3. 完整工作流程
- 接收字段列表:系统告诉方法"我需要这些字段的默认值"
- 检查上下文:查看是否有特殊参数影响默认值
- 计算默认值:基于上下文和业务逻辑计算
- 返回结果:提供字段名→默认值的映射
日历模块中的智能默认值示例
示例 1:自动关联源记录
# 当从销售订单创建会议时
if context.get('active_model') == 'sale.order':
defaults['res_model'] = 'sale.order'
defaults['res_model_id'] = self.env['ir.model']._get_id('sale.order')
defaults['res_id'] = context.get('active_id')
效果:会议自动关联到当前销售订单,无需手动选择。
示例 2:自动设置参会者
@api.model
def _default_partners(self):
"""当从联系人页面创建时,自动添加该联系人为参会者"""
partners = self.env.user.partner_id
active_id = self.env.context.get('active_id')
if self.env.context.get('active_model') == 'res.partner' and active_id:
partners |= self.env['res.partner'].browse(active_id)
return partners
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
if 'partner_ids' not in defaults and 'partner_ids' in fields:
defaults['partner_ids'] = [(6, 0, self._default_partners().ids)]
return defaults
效果:从联系人页面点击"安排会议",该联系人自动成为参会者。
示例 3:智能时间对齐
@api.model
def _default_start(self):
now = fields.Datetime.now()
return now + (datetime.min - now) % timedelta(minutes=30)
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
if 'start' not in defaults and 'start' in fields:
defaults['start'] = self._default_start()
if 'stop' not in defaults and 'stop' in fields:
defaults['stop'] = self._default_stop()
return defaults
效果:开始时间自动对齐到最近的30分钟间隔(如10:17 → 10:30)。
为什么需要 default_get?(业务价值)
1. 提升用户体验
- 减少点击次数:自动填充已知信息
- 降低错误率:减少手动输入
- 上下文感知:根据来源提供相关默认值
2. 支持复杂业务流程
- 跨模型关联:从销售订单创建发票时自动关联
- 动态默认值:基于用户、时间、位置等动态计算
- 向导集成:在多步向导中传递上下文
3. 实现"智能助手"功能
- 预测用户意图:从联系人页面创建 → 自动添加该联系人
- 提供合理建议:会议默认时长1小时
- 简化复杂操作:重复事件自动设置合理默认值
实战:自定义 default_get
场景:销售团队希望会议默认包含销售经理
@api.model
def default_get(self, fields):
# 先获取父类的默认值
defaults = super(CalendarEvent, self).default_get(fields)
# 获取销售经理
sales_manager = self.env.user.company_id.sales_manager_id
# 如果有销售经理且是销售团队成员创建会议
if sales_manager and self.env.user.has_group('sales_team.group_sale_salesman'):
# 获取当前参会者
current_partners = defaults.get('partner_ids', [])
# 确保是有效的命令格式
if not current_partners or not isinstance(current_partners[0], (list, tuple)):
current_partners = [(6, 0, current_partners)]
# 添加销售经理
partner_ids = current_partners[0][2] if current_partners else []
partner_ids.append(sales_manager.partner_id.id)
defaults['partner_ids'] = [(6, 0, list(set(partner_ids)))]
return defaults
效果:销售团队成员创建会议时,销售经理自动成为参会者。
高级技巧:基于时间的智能默认值
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
# 如果是下午3点后创建会议,默认安排在明天
now = datetime.now()
if now.hour >= 15:
tomorrow = now + timedelta(days=1)
# 设置为明天上午10点
defaults['start'] = tomorrow.replace(hour=10, minute=0, second=0, microsecond=0)
defaults['stop'] = defaults['start'] + timedelta(hours=1)
return defaults
常见问题与解决方案
问题 1:默认值不生效
原因:
- 字段未包含在
fields参数中 - 父类
default_get已经设置了该字段 - 表单视图中硬编码了默认值
解决方案:
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
# 确保只设置请求的字段
for field in ['name', 'description']:
if field in fields and field not in defaults:
defaults[field] = "Custom default"
return defaults
问题 2:上下文参数未正确传递
原因:
- 从JavaScript调用时未正确设置上下文
- 向导中丢失了原始上下文
解决方案:
# 在JavaScript中
this._rpc({
model: 'calendar.event',
method: 'create',
args: [{}],
context: Object.assign({}, this.context, {
default_res_model: 'sale.order',
default_res_id: orderId
})
});
最佳实践
1. 遵循"最少假设"原则
# 好的做法:只设置明确请求的字段
if 'start' in fields and 'start' not in defaults:
defaults['start'] = self._calculate_start_time()
# 避免:设置所有可能的字段
# defaults.setdefault('start', ...)
# defaults.setdefault('stop', ...)
2. 保留父类逻辑
# 总是先调用父类
defaults = super().default_get(fields)
# 然后添加/修改需要的默认值
if 'partner_ids' in fields and not defaults.get('partner_ids'):
defaults['partner_ids'] = self._get_default_partners()
3. 处理多种上下文场景
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
ctx = self.env.context
# 从联系人创建
if ctx.get('active_model') == 'res.partner':
defaults = self._handle_partner_context(defaults, ctx)
# 从销售订单创建
elif ctx.get('active_model') == 'sale.order':
defaults = self._handle_sale_order_context(defaults, ctx)
# 普通创建
else:
defaults = self._handle_normal_creation(defaults)
return defaults
4. 提供可扩展的钩子
@api.model
def default_get(self, fields):
defaults = super().default_get(fields)
# 允许子类扩展
self._update_default_values(defaults, fields)
return defaults
def _update_default_values(self, defaults, fields):
"""钩子方法,供继承类扩展默认值逻辑"""
pass
总结:default_get 的核心价值
default_get 是 Odoo 中用户体验的关键,它:
- 变"填空题"为"选择题"
不再让用户从零开始,而是提供合理的起点
- 理解用户意图
从哪里来(上下文)→ 想要什么(智能默认值)
- 减少重复劳动
自动填充已知信息,让用户专注关键内容
- 实现业务规则
通过默认值实施公司政策(如会议必须包含经理)
💡 关键认知 :
default_get不是简单的"设置默认值",而是 "理解用户上下文并提供智能建议" 的过程。好的default_get实现能让用户感觉系统"懂我",大幅提升使用体验。
在日历模块中,default_get 确保了无论用户从哪里创建会议(联系人页面、销售订单、直接创建),都能获得最相关的默认设置,使日程安排变得简单高效。