基于odoo17的设计模式详解---代理模式

大家好,我是你的Odoo技术伙伴。在Odoo开发中,我们每天都在和记录集(Recordsets)打交道,比如 self.env['res.partner'].search([...]) 或者 order.order_line。我们看似在直接操作数据库记录,但实际上,我们与真实数据之间,始终站着一位神通广大的"守卫"------这就是**代理模式(Proxy Pattern)**在Odoo中的体现。

今天,我们将揭开Odoo ORM这层神秘的面纱,深入探讨代理模式是如何作为Odoo数据访问的核心,为我们提供懒加载、权限控制、缓存等一系列强大功能的。

一、什么是代理模式?

让我们先从一个生活中的例子开始:使用银行卡取款。

你不会直接跑到银行的金库里去拿现金。相反,你会使用一张银行卡(代理 Proxy)在ATM机上操作。

  • 真实对象(Real Subject): 银行金库里的现金。
  • 代理(Proxy): 你的银行卡和ATM机系统。
  • 客户端(Client): 你。

这个代理为你做了很多事:

  • 访问控制(Access Control): 它会验证你的密码,检查你的账户余额,确保你不会取走不属于你的钱或超出限额。
  • 简化交互(Simplification): 你只需简单的插卡、输密码、按金额,复杂的后台账务处理、金库调动等都由它完成。
  • 附加功能(Additional Functionality): 它会记录你的交易日志,打印凭条。

转换成软件设计的语言:

代理模式为另一个对象提供一个替身或占位符,以控制对这个对象的访问。代理对象与真实对象实现相同的接口,使得客户端在不知道的情况下,可以透明地使用代理对象。

二、Odoo的ORM记录集:无处不在的智能代理

在Odoo中,最核心的代理模式应用就是ORM的记录集(Recordsets)。当你执行 partners = self.env['res.partner'].browse([1, 2, 3]) 时,你得到的 partners 变量并不是一个包含了所有 res.partner 数据的Python列表或字典。

它是一个代理对象

这个代理对象里面只包含了三样东西:

  • 模型的名称: 'res.partner'
  • 记录的ID列表: (1, 2, 3)
  • 环境(Environment): self.env,包含了上下文、用户、缓存等信息。

它是一个极其轻量级的"占位符"。

懒加载(Lazy Loading):代理模式的核心优势

现在,神奇的事情发生了。当你第一次尝试从这个代理对象中读取某个字段的值时,比如 partners[0].name,会发生什么?

  1. 触发代理 : 代理对象被"唤醒"。它发现自己还没有 id=1 的记录的 name 字段数据。
  2. 执行查询 : 它会利用自己的信息(模型名、ID列表、环境),向数据库发起一个SQL查询,例如 SELECT name, ... FROM res_partner WHERE id IN (1, 2, 3)。注意,为了效率,Odoo通常会一次性**预取(Prefetch)**该记录集所有记录的常用字段数据,而不仅仅是 name
  3. 填充缓存: 查询到的数据会被填充到代理对象内部的缓存中,同时也存入当前环境的全局缓存。
  4. 返回结果 : 将 name 字段的值返回给你。

当你第二次访问 partners[0].name,或者访问同样被预取了的 partners[0].email 时,代理会直接从自己的缓存中返回数据,不会再查询数据库。

这就是懒加载 :直到你真正需要数据时,才去数据库加载它。这极大地提升了性能,避免了在 searchbrowse 时就加载所有可能用到的字段,造成巨大的内存和I/O开销。

代理模式提供的附加功能

Odoo的记录集代理远不止懒加载这么简单,它像那位银行卡守卫一样,提供了众多关键的附加功能:

1. 访问权限控制(Access Control Proxy)

当你尝试读取或写入数据时,比如 partners.write({'name': 'New Name'}),代理会做如下检查:

  • 它会利用 self.env.user 信息,调用模型的 check_access_rights('write')check_access_rule('write') 方法。
  • 如果当前用户没有写入权限,或者违反了记录规则(Record Rules),操作将被拒绝,并抛出 AccessError 异常。

这个过程对开发者是完全透明的,你无需在每个 write 操作前手动检查权限。代理已经为你筑起了一道安全防线。

2. 缓存管理(Caching Proxy)

如前所述,代理对象在首次读取后会缓存数据。这个缓存是分层的:

  • 记录集缓存: 存在于代理对象自身,生命周期与该对象相同。
  • 环境缓存 : 存在于 self.env 中,在整个事务(request)期间有效。

这意味着,在同一次请求中,如果你通过不同的记录集代理访问到同一个记录的同一个字段,第二次访问会直接命中环境缓存,实现高效的数据共享。

3. 事务管理与脏数据跟踪

当你对一个记录集进行写操作时,例如 partners.name = 'Updated',代理并不会立刻执行 UPDATE SQL语句。它只是在自己的内部状态中将这条记录标记为"脏数据"(dirty)。

直到事务提交时,Odoo的ORM才会统一收集所有被标记的脏数据,一次性地将变更写入数据库。这同样是由代理在背后默默完成的。

三、代码中的体现:扩展方法即扩展代理行为

当我们通过 _inherit 扩展一个模型并重写其方法(如 writecreate)时,我们实际上是在扩展这个模型的代理行为。

示例:在写入联系人时记录审计日志

python 复制代码
# models/res_partner.py
from odoo import models, api

class ResPartnerAudit(models.Model):
    _inherit = 'res.partner'

    def write(self, vals):
        # 1. 代理执行附加的前置操作
        old_names = {p.id: p.name for p in self}

        # 2. 调用 super(),将请求传递给原始的代理行为(或链上的下一个代理)
        # 原始行为会处理权限检查、实际的数据更新等
        res = super(ResPartnerAudit, self).write(vals)

        # 3. 代理执行附加的后置操作
        if 'name' in vals:
            for partner in self:
                partner.message_post(body=f"Name changed from '{old_names[partner.id]}' to '{partner.name}'")
        
        return res

在这个例子中,我们的 ResPartnerAudit 类就好像一个更外层的代理,包裹住了Odoo原生的 res.partner 代理。当 write 被调用时:

  1. 我们的代码先执行(记录旧值)。
  2. 然后通过 super(),将控制权交还给内部的代理,让它去完成核心的写入、权限检查和缓存刷新工作。
  3. 内部代理完成后,控制权再次回到我们的代码,执行后置操作(记录chatter消息)。

整个过程就像层层嵌套的代理,每一层都可以添加自己的逻辑,同时又不破坏核心功能。

结论

代理模式是Odoo ORM的基石。它并非一个需要你手动创建的类,而是你日常使用的 records 对象本身。正是这个强大的代理机制,为我们带来了:

  • 高性能的懒加载和预取机制。
  • 透明且强制的安全权限控制。
  • 高效的缓存管理。
  • 事务性的写操作。

理解Odoo记录集本质上是一个代理,会让你对Odoo的性能优化、权限控制和事务处理有更深刻的认识。你会明白,为什么我们应该尽量操作记录集而不是ID列表,为什么 super() 在重写ORM方法中如此重要。

下一次,当你写下 order.partner_id.name 时,请记住,你正在与一个聪明、高效、安全的代理守卫对话,它正在为你打理着与数据库之间的一切复杂事务。

相关推荐
缘来是庄3 小时前
设计模式之访问者模式
java·设计模式·访问者模式
hqxstudying6 小时前
Java创建型模式---单例模式
java·数据结构·设计模式·代码规范
花好月圆春祺夏安7 小时前
基于odoo17的设计模式详解---装饰模式
数据库·python·设计模式
fie88897 小时前
浅谈几种js设计模式
开发语言·javascript·设计模式
哆啦A梦的口袋呀7 小时前
《深入设计模式》模式结构汇总
设计模式
花好月圆春祺夏安7 小时前
基于odoo17的设计模式详解---单例模式
单例模式·设计模式
在未来等你10 小时前
设计模式精讲 Day 22:模板方法模式(Template Method Pattern)
设计模式·模板方法模式·软件架构·java开发·面向对象设计·设计模式实战·java应用开发
Small black human1 天前
设计模式-应用分层
设计模式
码农秋1 天前
设计模式系列(10):结构型模式 - 桥接模式(Bridge)
设计模式·桥接模式