PAYATHON 2026

如何在 Odoo 中按 id 获取上一条或最新数据?

支付老李

结论

问题在于 max(ids)。self.search() 返回的是 Odoo recordset,不是普通的整数 ID 列表。直接对 recordset 使用 Python 的 max(),无法可靠地找到 ID 最大的记录。

可以让数据库按 ID 排序,并且只取第一条:

last_bill = self.search(
    [('meter_no', '=', self.meter_no.id)],
    order='id desc',
    limit=1,
)

再读取这条记录的 current_reading。

推荐写法

创建新账单时,当前记录尚未保存,search() 不会查到它。因此,可以直接查询该电表最后创建的账单:

@api.onchange('meter_no')
def onchange_meter_no(self):
    self.ensure_one()

    self.prev_reading = 0.0

    if not self.meter_no:
        return

    last_bill = self.search(
        [('meter_no', '=', self.meter_no.id)],
        order='id desc',
        limit=1,
    )

    if last_bill:
        self.prev_reading = last_bill.current_reading

这段代码中:

  • order='id desc' 按 ID 从大到小排序。
  • limit=1 只返回第一条,也就是 ID 最大的记录。
  • last_bill 仍然是 recordset,可以直接读取 last_bill.current_reading。

为什么原来的 max() 不可靠

下面的变量虽然名为 ids,实际得到的却不是 ID 列表:

ids = self.search([('meter_no', '=', self.meter_no.id)])

它返回的是 billing 模型的 recordset,其中的元素是 Odoo 记录对象。因此,执行:

max(ids)

比较的是记录对象,并不等同于:

max(ids.ids)

即使改用 max(ids.ids),仍需通过 browse() 取得对应记录:

bills = self.search([('meter_no', '=', self.meter_no.id)])
last_bill_id = max(bills.ids) if bills else False
last_bill = self.browse(last_bill_id) if last_bill_id else False

这种写法可以工作,但它会先查询所有匹配记录,再由 Python 计算最大值,过程更绕,也没有必要。使用 order 和 limit 更直接,效率也更高。

编辑已有账单时需要排除当前记录

如果 onchange 也会在编辑已有账单时触发,搜索结果可能包含当前账单。此时需要排除当前记录,否则可能会把它自己的 current_reading 填入 prev_reading。

可以通过 _origin 获取数据库中原始记录的 ID:

@api.onchange('meter_no')
def onchange_meter_no(self):
    self.ensure_one()

    self.prev_reading = 0.0

    if not self.meter_no:
        return

    domain = [
        ('meter_no', '=', self.meter_no.id),
    ]

    if self._origin and self._origin.id:
        domain.append(('id', '!=', self._origin.id))

    last_bill = self.search(
        domain,
        order='id desc',
        limit=1,
    )

    if last_bill:
        self.prev_reading = last_bill.current_reading

新建且尚未保存的账单通常没有 self._origin.id,因此不会添加这项排除条件。

“最新数据”不一定等于”最大 ID”

如果业务中的”最新”是指最后创建的账单,按 id desc 排序通常可以满足要求。但如果账单允许补录或倒序录入,或者需要根据计费日期判断,就应该按业务日期排序。假设字段名是 billing_date:

last_bill = self.search(
    [('meter_no', '=', self.meter_no.id)],
    order='billing_date desc, id desc',
    limit=1,
)

如果”最新”是指最后修改的记录,可以考虑:

order='write_date desc, id desc'

不过,任何字段发生修改都会更新 write_date,它不一定代表最后一期账单。因此,通常应优先使用明确的计费日期字段。

其他注意事项

不要这样赋值:

self.prev_reading = last_bill.current_reading or None

0.0 在 Python 中是假值,会被替换成 None。对于 Odoo 的数值字段,直接赋值即可:

self.prev_reading = last_bill.current_reading

没有历史账单时,可以明确设置为 0.0:

self.prev_reading = last_bill.current_reading if last_bill else 0.0

另外,onchange 只能查询已经写入数据库的数据。如果上一张账单的 current_reading 尚未保存,新账单就无法通过 search() 取得该值。

备注:内容仅供参考。