Registry

Registry是odoo中用来处理每个数据库中模型名称和模型类对应关系的工具,每个数据库只有一个Registry的实例。

类属性

Registry有几个比较重要的类属性

registries

registries负责维护数据库与registry实例的映射关系,也就说每个数据库都可以通过registries[dbname]的方式获取本数据库对应的registry实例。

registries的内部实现使用了LRU(最近最少使用)机制,可以通过配置文件的registry_lru_size参数来指定队列的长度。由于Windows不支持支持虚拟内存上限,因此Windows上的LRU队列长度是个固定值42。而对于Linux系统,默认分配给每个registry实例的内存为15M,所以队列的长度由虚拟的存储上限除以15计算而来。

odoo默认的虚拟内存上限是2048M,所以默认的LRU的队列长度为 2048/15 = 136

类方法new

Registry的new方法用来创建并返回给出的数据库名称的Registry实例。

@classmethod
def new(cls, db_name, force_demo=False, status=None, update_module=False):
    """ Create and return a new registry for the given database name. """
    with cls._lock:
        with odoo.api.Environment.manage():
            registry = object.__new__(cls)
            registry.init(db_name)

            # Initializing a registry will call general code which will in
            # turn call Registry() to obtain the registry being initialized.
            # Make it available in the registries dictionary then remove it
            # if an exception is raised.
            cls.delete(db_name)
            cls.registries[db_name] = registry
            try:
                registry.setup_signaling()
                # This should be a method on Registry
                try:
                    odoo.modules.load_modules(registry._db, force_demo, status, update_module)
                except Exception:
                    odoo.modules.reset_modules_state(db_name)
                    raise
            except Exception:
                _logger.error('Failed to load registry')
                del cls.registries[db_name]
                raise

            # load_modules() above can replace the registry by calling
            # indirectly new() again (when modules have to be uninstalled).
            # Yeah, crazy.
            registry = cls.registries[db_name]

        registry._init = False
        registry.ready = True
        registry.registry_invalidated = bool(update_module)

    return registry

从代码中可以看出,Registry在new方法中完成了实例registry与数据库dbname的映射,然后调用了moduels模块的load_modules方法将模块注册到registry关联的db中。

实例属性

registries和new方法主要用来处理dbname和regstry的关系,而registry的任务则是处理model和modelclass的关系,下面我们就来看看它的实例属性和方法。

  • registry: 使用实例属性models来维护model和model实例的对应关系。
  • db_name:来记录当前数据库名称。
  • loaded: 标识是否已经加载所有模块。
  • ready: 标识是否一切准备就绪。

Registry中有个load方法用来加载给出的模块中的所有改动过的模型的名称,然后再调用模型(model)的_build_model方法,将模型注册到registry中。

def load(self, cr, module):
    """ Load a given module in the registry, and return the names of the
    modified models.

    At the Python level, the modules are already loaded, but not yet on a
    per-registry level. This method populates a registry with the given
    modules, i.e. it instanciates all the classes of a the given module
    and registers them in the registry.

    """
    from .. import models

    # clear cache to ensure consistency, but do not signal it
    self.__cache.clear()

    lazy_property.reset_all(self)

    # Instantiate registered classes (via the MetaModel automatic discovery
    # or via explicit constructor call), and add them to the pool.
    model_names = []
    for cls in models.MetaModel.module_to_models.get(module.name, []):
        # models register themselves in self.models
        model = cls._build_model(self, cr)
        model_names.append(model._name)

    return self.descendants(model_names, '_inherit', '_inherits')

modules的加载

前面提到,Registry在new方法中完成了registry与数据库名称的映射,然后向该数据库中加载了模块。那么模块是如何加载的呢?

在modules的load_modules方法内部,会首选确认当前数据库是否进行了初始化,如果没有则会提示用户进行初始化操作。之后,系统会检查命令参数中是否指定了要更新全部的模块,如果是,则更新全部的模块。(-u all)。

实际上,更新全部模块的效果跟更新base模块的效果一样,其本质都是将ir_module_module表中base模块的状态设置为to upgrade。

在odoo中base模块是其他模块的依赖模块,也是加载过程中第一个被加载的模块。加载完base模块之后开始加载其他模块,如果有需要更新的模块,则进行升级更新。

所有模块加载完成以后,调用registry的setup_models方法进行模型的挂载操作。

def setup_models(self, cr):
    """ Complete the setup of models.
        This must be called after loading modules and before using the ORM.
    """
    lazy_property.reset_all(self)
    env = odoo.api.Environment(cr, SUPERUSER_ID, {})

    # add manual models
    if self._init_modules:
        env['ir.model']._add_manual_models()

    # prepare the setup on all models
    models = list(env.values())
    for model in models:
        model._prepare_setup()

    # do the actual setup from a clean state
    self._m2m = {}
    for model in models:
        model._setup_base()

    for model in models:
        model._setup_fields()

    for model in models:
        model._setup_complete()

    self.registry_invalidated = True

从代码中可以看出,setup_models方法主要处理模型的挂载任务,首先执行挂载前方法_prepare_setup,然后依次执行了_setup_base,_setup_fields,_setup_complete三个方法完成挂载。

模型挂载完成之后,模块将数据库的结构进行了升级。

最后,完成安装和清理任务。

odoo中有些参数是只能在命令行下输入而不能出现在配置文件中的,这样的参数有: 'publisher_warranty_url', 'load_language', 'root_path', 'init', 'save', 'config', 'update', 'stop_after_init', 'dev_mode', 'shell_interface'

初始化models

模块再挂载完模型之后,将会调用Registry的init_model方法将模型初始化。

def init_models(self, cr, model_names, context):
    """ Initialize a list of models (given by their name). Call methods
        ``_auto_init`` and ``init`` on each model to create or update the
        database tables supporting the models.

        The ``context`` may contain the following items:
            - ``module``: the name of the module being installed/updated, if any;
            - ``update_custom_fields``: whether custom fields should be updated.
    """
    if 'module' in context:
        _logger.info('module %s: creating or updating database tables', context['module'])
    elif context.get('models_to_check', False):
        _logger.info("verifying fields for every extended model")

    env = odoo.api.Environment(cr, SUPERUSER_ID, context)
    models = [env[model_name] for model_name in model_names]

    for model in models:
        model._auto_init()
        model.init()

    while self._post_init_queue:
        func = self._post_init_queue.popleft()
        func()

    if models:
        models[0].recompute()

模型的初始化方法内部实例化了一个环境变量env,然后将模型列表中模型名称一一转换成模型对象,执行其初始化方法。前面我们在讲环境变量的时候提到过,模型的env变量就是此时赋值给模型的实例的。

results matching ""

    No results matching ""