You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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即可。


额外提醒

  1. 模板ID要统一:你代码里写的email_template_1和XML里的email_template_t不一致,一定要改成同一个,否则会找不到模板!
  2. 变量名错误:原代码里的location_changes['power_difference']应该是location_power_changes['power_difference'],已经在修正后的代码里改过来了。
  3. 邮件发送时机:如果用send_mail()的force_send=True,邮件会立即发送;如果去掉这个参数,会进入Odoo的邮件队列,由系统定时发送,适合批量邮件场景。

内容的提问来源于stack exchange,提问作者fueggit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:29:36