env在odoo中是一个非常重要的概念,它是一个全局变量,保存了odoo运行环境的重要信息,env分为前端和后端
在web\static\src\env.js中定义,包含两个重要的对象:
这个文件非常重要,它定义了两个函数
makeEnv: 初始化env对象。
export function makeEnv() {
return {
bus: new EventBus(),
services: {},
debug: odoo.debug,
get isSmall() {
throw new Error("UI service not initialized!");
},
};
}
startServices: 启动所有的Service, 这个函数的实现非常巧妙,因为它要在不考虑服务加载顺序的前提下解决服务之间的依赖问题。 其中细节,值得一读。
不过这里只有这两个函数的定义,并没有执行这两个函数, 真正执行这两个函数的位置在 start.js中
const env = makeEnv();
await startServices(env);
服务启动完后, 会放入env的services对象中。 注意其中放的不是服务对象本身,而是start函数的返回值。
后端的env在odoo\api.py中定义
"""The Odoo API module defines Odoo Environments and method decorators.
.. todo:: Document this module
"""
__all__ = [
'Environment',
'Meta',
'model',
'constrains', 'depends', 'onchange', 'returns',
'call_kw',
]
文件的注释中说明了两点:
1、这个文件定义了odoo环境和方法装饰器
2、todo: 要为这个模块写文档(啥时候写?)
默认导出的对象,odoo后端最重要的一些对象
‘Environment’,
‘Meta’,
‘model’,
‘constrains’, ‘depends’, ‘onchange’, ‘returns’,
‘call_kw’,
from collections.abc import Mapping
class Environment(Mapping):
""" The environment stores various contextual data used by the ORM:
- :attr:`cr`: the current database cursor (for database queries);
- :attr:`uid`: the current user id (for access rights checks);
- :attr:`context`: the current context dictionary (arbitrary metadata);
- :attr:`su`: whether in superuser mode.
It provides access to the registry by implementing a mapping from model
names to models. It also holds a cache for records, and a data
structure to manage recomputations.
"""
def reset(self):
""" Reset the transaction, see :meth:`Transaction.reset`. """
self.transaction.reset()
def __new__(cls, cr, uid, context, su=False):
if uid == SUPERUSER_ID:
su = True
assert context is not None
args = (cr, uid, context, su)
# determine transaction object
transaction = cr.transaction
if transaction is None:
transaction = cr.transaction = Transaction(Registry(cr.dbname))
# if env already exists, return it
for env in transaction.envs:
if env.args == args:
return env
# otherwise create environment, and add it in the set
self = object.__new__(cls)
args = (cr, uid, frozendict(context), su)
self.cr, self.uid, self.context, self.su = self.args = args
self.transaction = self.all = transaction
self.registry = transaction.registry
self.cache = transaction.cache
self._cache_key = {} # memo {field: cache_key}
self._protected = transaction.protected
transaction.envs.add(self)
return self
env对象存储了不同类型的上下文数据被ORM使用,
- :attr:cr
: the current database cursor (for database queries);
- :attr:uid
: the current user id (for access rights checks);
- :attr:context
: the current context dictionary (arbitrary metadata);
- :attr:su
: whether in superuser mode.
env继承自 collection 的Mapping
在文件的末尾有两句import有点意思
# keep those imports here in order to handle cyclic dependencies correctly
from odoo import SUPERUSER_ID
from odoo.modules.registry import Registry
之所以放在文件末尾,是为了正确的处理循环依赖。 姑且先不管的原理是啥了,不过这里引入了一个重要的对象 Regisitry ——后端的注册表
class Registry(Mapping):
""" Model registry for a particular database.
The registry is essentially a mapping between model names and model classes.
There is one registry instance per database.
"""
_lock = threading.RLock()
_saved_lock = None
@lazy_classproperty
def registries(cls):
""" A mapping from database names to registries. """
size = config.get('registry_lru_size', None)
if not size:
# Size the LRU depending of the memory limits
if os.name != 'posix':
# cannot specify the memory limit soft on windows...
size = 42
else:
# A registry takes 10MB of memory on average, so we reserve
# 10Mb (registry) + 5Mb (working memory) per registry
avgsz = 15 * 1024 * 1024
size = int(config['limit_memory_soft'] / avgsz)
return LRU(size)
def __new__(cls, db_name):
""" Return the registry for the given database name."""
with cls._lock:
try:
return cls.registries[db_name]
except KeyError:
return cls.new(db_name)
finally:
# set db tracker - cleaned up at the WSGI dispatching phase in
# odoo.http.root
threading.current_thread().dbname = db_name
这里不想写的过于深入,先摸清系统的大概框架,在需要的时候再去扣细节,这才是学习的正确方法 。