Odoo自动月度报表邮件发送问题:self.id为空致模板渲染失败
问题拆解与解决方案
你在做Odoo月度库存报表自动发送功能时,遇到了两个核心问题:一是@api.model装饰的方法里self.id为False,导致邮件模板无法绑定记录渲染;二是模板里直接调用object.get_data()会重复执行数据计算,容易引发数据不一致。下面给你针对性的解决办法:
先搞懂为什么self.id是False
你的get_data()用了@api.model装饰器,这个装饰器下的self代表的是**stock.location模型本身**,而不是某一条具体的库存位置记录——模型本身没有id属性,所以self.id自然是False。这也是send_mail()报错的根本原因。
方案1:绑定真实记录发送(适配现有模板配置)
既然你的邮件模板model_id设置为stock.location,那我们只需要给send_mail()传一条真实的库存位置记录ID就行,同时把计算好的records通过上下文传给模板,避免重复计算:
修正后的get_data()方法
@api.model def get_data(self): locations = self.env['stock.location'].search([('id','child_of',[361]),('usage','=','view')]) records = [] for location in locations: power = location.power location.power_difference = location.last_month_power - power location.last_month_power = power # 修正原代码的变量名错误 location_power_changes = {'power_difference': location.power_difference} records.append(location_power_changes) # 注意模板ID要和XML里的对应!原代码里的email_template_1与XML的email_template_t不一致,已统一 template = self.env.ref('t_stock.email_template_t') if locations: # 把records存入上下文,同时传递第一个库存位置的ID给send_mail template.with_context(records=records).send_mail(locations[0].id, force_send=True)
修正后的邮件模板body_html
直接从上下文取records,不用再调用get_data():
<field name="body_html"><![CDATA[ <p>Test</p> <table> <tbody> <tr> <th>T1</th> </tr> % if records: % for values in records: <tr> <td>${values['power_difference']}</td> </tr> % endfor % endif </tbody> </table><br> ]]></field>
方案2:手动渲染模板发送(更灵活,无需绑定特定记录)
如果你的报表不需要和任何stock.location记录绑定,可以跳过send_mail(),直接手动渲染模板内容,用mail.mail发送,这样更灵活:
修正后的get_data()方法
@api.model def get_data(self): locations = self.env['stock.location'].search([('id','child_of',[361]),('usage','=','view')]) records = [] for location in locations: power = location.power location.power_difference = location.last_month_power - power location.last_month_power = power location_power_changes = {'power_difference': location.power_difference} records.append(location_power_changes) template = self.env.ref('t_stock.email_template_t') # 手动渲染模板内容,传入自定义上下文 rendered_body = template._render_template( template.body_html, 'stock.location', None, context={'records': records} ) rendered_subject = template._render_template( template.subject, 'stock.location', None, context={'records': records} ) # 创建邮件并发送 mail_vals = { 'email_from': template.email_from, 'email_to': template.email_to, 'subject': rendered_subject, 'body_html': rendered_body, 'model': False, # 不关联任何模型 'res_id': False, } self.env['mail.mail'].create(mail_vals).send()
模板的修改和方案1一致,直接用上下文的records即可。
额外提醒
- 模板ID要统一:你代码里写的
email_template_1和XML里的email_template_t不一致,一定要改成同一个,否则会找不到模板! - 变量名错误:原代码里的
location_changes['power_difference']应该是location_power_changes['power_difference'],已经在修正后的代码里改过来了。 - 邮件发送时机:如果用
send_mail()的force_send=True,邮件会立即发送;如果去掉这个参数,会进入Odoo的邮件队列,由系统定时发送,适合批量邮件场景。
内容的提问来源于stack exchange,提问作者fueggit
相关产品推荐
相关产品推荐

