第十二章 环境变量

Odoo的环境变量(Enviroment),是一种封装了数据库访问技术,同时对数据缓存和重新计算逻辑进行管理的可哈希对象。环境变量为ORM提供了底层技术支持,是odoo开发过程中经常见到且非常重要的一个概念。

从环境变量的定义中我们就可以看出其三种用途

  • ORM数据访问层技术封装
  • 缓存管理
  • 需要重复计算的数据结构管理

其中最常见的用途便是ORM的数据访问层技术,因为我们在开发中几乎每天都在用到self.env对象,我们用它来获取想要的对象模型,然后进行逻辑处理。本章将揭开环境变量的面纱,让我们认清楚这个最熟悉的陌生对象。

环境变量的数据结构

环境变量由于继承自Python的Mapping对象而来,所以其本质上就是一种字典

环境变量的创建

环境变量在进行创建的时候,会检查当前进行的事务(transaction)的环境变量集合中是否存在相同的环境变量对象,如果存在则直接返回存在的对象,只有不存在时才会进行创建操作。

环境变量的创建过程,首先会将数据库访问参数、上下文变量和超级用户模式进行赋值。也因此环境变量从“出生”时就拥有cr, uid,context和su四个重要的属性。这四个属性的意思分别是:

  • cr: 数据库访问游标对象
  • uid: 当前环境的用户uid
  • context: 当前上下文对象
  • su: 是否为超级用户模式,默认为False

环境变量能够访问数据库注册中心和能够管理缓存,其本质上是因为借助了事务对象的属性。而事务对象则是通过cr对象传入到环境变量对象中的。也因此,环境变量对象还有一下属性:

  • transcation: 事务对象
  • registry: 注册中心对象
  • cache: 缓存对象

13.0及之前的版本中,还有一个变量envs, 它使用了web服务器(werkzurg)中名为Enviroment的本地变量, 所有新生成的环境变量实例都会存储在envs中. 到了15.0版本, envs被移动到了事务中.

快速访问变量

为了开发方便,环境变量对象提供了几个常用的变量:

  • user: 当前用户
  • company: 当前公司
  • companies: 当前用户能够访问的公司集合
  • self.env.lang: 获取当前用户的语言代码

company对象从13.0开始引入, 之前版本没有此变量

快速获取模型对象原理

我们在开发过程中最常用的一种代码就是:

self.env['sale.order'].browse(1)

那么环境变量对象是如何获取模型的呢?或者说env['sale.order']的内部机制是怎样的呢?

我们知道env是一个字典,env['sale.order']应该是从字典中取出key为sale.order的值。实际上,环境变量对象中并没有从自身的字典中取值,而是从注册中心对象中取的值,并使用低阶的_browse方法获取了一个模型对象。

def __getitem__(self, model_name):
    """ Return an empty recordset from the given model. """
    return self.registry[model_name]._browse(self, (), ())

其实,不仅仅是取值方法使用了注册中心的取值方法,环境变量对象的迭代、获取长度等操作也是用了注册中心对象的方法:

def __iter__(self):
    """ Return an iterator on model names. """
    return iter(self.registry)

def __len__(self):
    """ Return the size of the model registry. """
    return len(self.registry)

def __contains__(self, model_name):
    """ Test whether the given model exists. """
    return model_name in self.registry

ref方法原理

同样的,我们在使用过程中可以通过self.env.ref()获取xmlid的相应对象,ref方法的本质是调用了ir.model.data对象的xmlid_to_object方法:

def ref(self, xml_id, raise_if_not_found=True):
    """Return the record corresponding to the given ``xml_id``."""
    return self['ir.model.data'].xmlid_to_object(xml_id, raise_if_not_found=raise_if_not_found)

常用方法

除了上述属性和方法,环境变量对象还提供了一系列方法来帮助我们快速判断操作:

  • is_superuser(): 判断当前用户是否为超级用户。
  • is_admin(): 用来判断当前用户是否是管理员账号。
  • is_system(): 用来判断当前用户是否是系统用户。

所谓超级账号,是指uid为1的账号,12.0起即为系统的机器人账号。所谓管理员账号,即系统中拥有访问权限的账号。而系统账号指的是具有设置权限的用户。从源码中我们也可以看出,都是利用了user属性的私有方法_is_admin和_is_system来实现的。

环境变量在模型中的加载时机

我们知道,在模型中写业务代码时,可以直接使用下面的代码来使用环境变量对象:

self.env

可是,env变量是什么时候赋给当前对象的呢?

我们在模型的源代码中只找到了env变量的声明语句,却不见它的赋值。实际上,这是因为真正的赋值语句被隐藏在了_browse方法中。

@classmethod
def _browse(cls, env, ids, prefetch_ids):
    """ Create a recordset instance.

    :param env: an environment
    :param ids: a tuple of record ids
    :param prefetch_ids: a collection of record ids (for prefetching)
    """
    records = object.__new__(cls)
    records.env = env
    records._ids = ids
    records._prefetch_ids = prefetch_ids
    return records

_browse方法接收三个参数:

  • env: 环境变量
  • ids: 记录集
  • prefetch_ids: 要预先获取的ids

结合前面讲的,我们可以总结出来,env的取值操作会调用低阶的_browse方法,将自身传入用于获取记录集对象(records)

而odoo系统在启动时会将已经注册到数据库中的模型ir.model进行实例初始化,就是在这时,系统将最开始初始化的环境变量通过取值操作将自身传给了返回的模型对象。

def init_models(self, cr, model_names, context, install=True):
    ...
    models = [env[model_name] for model_name in model_names]
    ...

实际上我们在odoo中的开发,大多数时候都是在模型列表加载完成后进行的,此时早已完成赋值操作,也因此我们可以直接使用例如self.env的方式操作环境变量对象。

15.0开始,Environment对象中的envs属性已停用, 使用cr.transaction或env.transaction替代

results matching ""

    No results matching ""