zoukankan      html  css  js  c++  java
  • Odoo中的Environment对象

    接触过odoo开发的同学一定对Environment对象不陌生,但是你真的了解odoo中的Environment对象吗,不妨看看你能否回答出下面的问题:

    1. 我们都知道 sale_order_obj = self.env['sale.order'] 可以获取到销售订单的模型,但它背后的原理是什么?

    2. 我们可以通过self.env.user获取当前用户,self.env.company可以获取当前公司,那么还有什么其他的变量可以直接使用吗?

    3. 为什么self.env.ref可以获取到xmlid对应的对象?

    4. self.env默认是当前公司,如果想要切换到另外一个公司,应该怎么做?如果你知道答案,那么我再问,当前版本(14.0)中那个方法还有效吗?

    5. 我们知道self.env.context是个不可变值,那么如果我们就是想要改变context中的某些值,应该怎么做?其内部原理是怎样的?

    6. 模型对象中的env变量(即我们常用的self.env)是什么时候进行初始化赋值的?










    如果你对上述6个问题的都了然于胸,那么你肯定是odoo领域的大牛,原谅我在您面前班门弄斧了,下面的文章就不用浪费时间了。如果你对问题的答案摸棱两可或是一头雾水,那么你可以接着往下读,看看笔者能否给你一次讲个明白。

    Environment是什么?

    odoo中的environment对象是从旧版本API演化到新版本API的产物,它封装了旧版本中一些琐碎的变量,例如cr, uid, ids, context等等。它的主要作用是封装ORM的环境变量,提供新版API模型的快速访问方法,以及管理一些需要计算的数据结构。

    不知道读者是否注意过,我们在定义一个模型的时候,只定义了模型的字段数据和必要的方法,缺从未对模型进行过实例化(如果你有过Python编程经验,应该知道类只有被实例之后才可以使用)。既然odoo不需要我们手动进行实例化,那么它是在什么时候进行实例化的呢?

    答案是odoo在启动之后,即对所有记录在案(数据库)的模型进行了注册,将模型名称与模型类的对应关系维护起来,当我们需要的时候就通过叫名字的方法将模型类调出来供我们使用。这种理念有没有很熟悉?没错,它很像我们数据库操作中线程池的概念,早期的odoo版本使用的变量名就是pool,随着版本的迭代,pool在系统中已经不多见了,取而代之的就是我们今天的主角—Environment。

    事实上,我们在使用self.env['sale.order']方法获取到的即模型sale.order的实例,之后我们使用ORM的browse或者search方法对返回的记录集进行操作。

    Environment对象的变量

    我们知道可以通过self.env来或者模型实例,也可以获取当前用户,那么Environment对象有哪些常用的数量呢?

    下面是笔者从代码中总结出来的常用的变量:

    • self.cr: 数据库查询cursor
    • self.uid: 当前用户的uid
    • self.context: 当前用户的上下文
    • self.su: 是否是超级用户
    • self.env.user: 获取当前用户对象
    • self.env.company: 获取当前用户的公司对象
    • self.env.companies: 获取当前用户可访问的所有公司
    • self.env.lang: 获取当前用户的语言代码

    另外还有几个可以用来判断当前用户角色的方法:

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

    Environment获取模型实例的原理

    Envronment对象在实例化的时候会实例化一个Registry对象,Registry对象是odoo用来管理注册模型的类,Environment对象使用Registery的实例通过传入的模型名称获取模型的类名称,然后再实例化一个空的记录集返回给调用者使用。

    ref方法的原理

    Environment的ref方法实际上调用的是ir.model.data的xmlid_to_object方法,做了简单的封装,以方便我们进行调用。

    多公司条件的下的环境变量

    如果启用了多公司,那么我们的self.env默认为当前的用户所在的公司环境。假设我们系统中目前有两个公司A和B,如果当前公司是A,想要操作B公司的数据,应该如何处理?

    在13.0版本中,我们可以使用with_context(force_company=B公司的id)的方式进行强制的公司环境切换。但是14.0中已经弃用了force_company,取而代之的是使用with_company方法。

    那么问题来了,with_context是什么,有什么作用?

    with_context

    我们知道,self.env.context是个不可变的字典,我们不能直接修改它的内容。如果某个需求要求你必须将它的内容改掉,怎么办?

    答案也很简单,既然你不让我改,那我再重新制造一份新的副本不就可以了。这就是with_context的使用场景。

    with_context方法将传入的上下文变量,使用之前的记录ids,重新生成了一份新的记录集,返回给调用方使用。

    Environment的加载机制

    那么现在来讨论最后一个问题,self.env是何时赋值给self的?

    这个问题的答案非常隐晦,笔者翻遍了model和api的源码也没找到BaseModel中给env变量赋值的语句。起初,笔者以为既然BaseModel中没有,那么它可能隐藏在元类MetaModel和Meta中,后来证明BaseModel和Meta中也没有。

    实际上,odoo在启动过程中对数据库中的模型(ir.model)进行了初始化,而初始化这一步骤中实例化了一个env对象,但这里没有model.env = env这种显式的赋值语句,实际上的赋值操作隐藏在了env[model_name]这里。

    env[model_name]方法内部将当前的env变量传给了model的实例,也因此,我们在后续的操作中可以使用self.env直接调用环境变量。

    一般情况下,我们不需要操心env的加载时机,因为对于常规的开发而言,操作时env变量已经是就绪状态,直接调用即可。

    更详细的内容请关注公众号: odoohub 开发指南第二部分第十二章

  • 相关阅读:
    python基础之流程控制
    多线程---阻塞队列
    多线程---线程同步
    多线程---线程实现
    多线程start();之后会开辟新的栈空间
    java中使用String的split分隔字符串注意事项
    IO流
    java中的多态
    关于java中的接口
    关于final关键字
  • 原文地址:https://www.cnblogs.com/kfx2007/p/14927361.html
Copyright © 2011-2022 走看看