Skip to content

Instantly share code, notes, and snippets.

@hbrls
Last active December 4, 2015 03:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hbrls/b1ccb01a6c7d3b0ef5c0 to your computer and use it in GitHub Desktop.
Save hbrls/b1ccb01a6c7d3b0ef5c0 to your computer and use it in GitHub Desktop.

openerp-server 的启动过程

./openerp-server -c /etc/openerp-server.conf

  1. ~/cli/server.py, ln 320, main(args)

  2. ~/cli/server.py, ln 366, openerp.service.start_services_workers()openerp.service.start_services()

openerp.service.start_services

  1. start_internal() 1.1 openerp.osv.osv.start_object_proxy() 1.2 web_services.start_service() 1.3 load_server_wide_modules(),openerp.modules.module.load_openerp_module 1.4 加载 web 后会通过 postload 去执行 openerp.addons.web.http.wsgi_postload 1.5 在初始化的过程中会更新 openerp.addons.web.http 里的 addons_module, addons_manifest, controllers_class, controllers_class_path, controllers_object, controllers_object_path, controllers_path 这些全局可见的变量,魔改的想象空间很大 1.6 openerp.wsgi.register_wsgi_handler(Root()) 生成一个 wsgi app 并注册,实际的 app 是 Root.dispatch 1.7 Root.__init__self.load_addons,凡是在路径下的所有 addon 都会被加载,也就是说所有语句都会被执行,好可怕 1.8 _cp_path 相当于 Blueprint,其后的部分匹配为 method 1.9 没有区分 GET POST 等,而且按照注释看来,无论参数都会被统一处理到同一个地方

  2. wsgi_server.start_service()

  3. cron.start_service()

openerp.servier.wsgi_server.serve()

通过多进程起的,暂时没有打到断点

i18n

  1. 当前用户使用的语言 _oe_instance.session.user_context.lang

模块的加载顺序(js)

  1. 对于oe 网页服务来说,base 不是基础模块,web 才是(cli 的情况不清楚) 1.1 更确切的说,默认值是 ['web', 'web_kanban'] // ~/openerp/tools/config.py, line 526

  2. 加载过程的主要代码在 ~/openerp/addons/web/controllers/main.py, line 247, module_boot 及相关代码 2.1. 首先加载网页服务的基础模块,然后加载本数据库中已安装的 addons 2.2. 网页服务的基础模块,即 ['web', 'web_kanban'] 确定是最先加载的 2.3. 已安装的 addons 按照依赖关系,最终的加载顺序是按照 Topsort 排序的结果;该排序的结果是是“被依赖模块”确定在“依赖模块”的前面;但考虑到复杂的深层依赖关系,以及实际安装 addon 的顺序不同,这个排序的结果实际上是不稳定的;也就是说除了好好分析依赖关系和写清楚依赖关系,没有其它途径可以预期最终的加载行为

  3. 然后根据 addon 的顺序,以及 addon__openerp__.py 中写清楚的静态文件加载顺序进行依次加载 3.1 如果 AddonA 依赖于 AddonB,那么 AddonB 中设定的 js, css 全部在 AddonA 之前加载 3.2 如果在 AddonA 中设定 'js': ['1.js', '2.js'],那么 1.js2.js 之前加载

  4. 最终的模块加载效果可以在 window.openerp.instances.instance0 里查看

  5. 不是所有已安装的模块都会被“加载”,比如 product 就不会出现在上面这个列表里

自定义视图

  1. 官方的原生视图包括 tree/list, formkanban 是扩展出来的,所以如果要自定义视图,主要参考 kanban 的实现

  2. 主要需要 override 的属性和方法如下(命名规则随手写的,不保证对):

    instance.nt_my_addon.CustomView = instance.web.View.extend({
        searchable: false,
    
        template: 'nt_my_addon.custom_view_tpl',
    
        display_name: 'Custom View',
    
        view_type: 'custom_view',
    
        init: function (parent, dataset, view_id, options) {},
    
        start : function () {
            // 不要去 call this._super,完全自己控制
        },
    
        do_show: function () {
            // 详见 nt_bwk_flowchart
            this._super.apply(this, arguments);
        },
    
        destroy: function () {
        },
    });
    
    instance.web.views.add('CustomView', 'instance.nt_my_addon.CustomeView');
    
  3. 原始菜单一般会指向 tree view,并且不知道 custom view 的存在,因此需要自定义菜单,并覆盖或隐藏掉原始菜单

    <record id="nt_my_addon_custom_view" model="ir.ui.view">
        <field name="name">nt.my_addon.custom_view</field>
        <field name="model">model.to.display</field>
    </record>
    
    <record id="nt_my_addon_tree_action">
        <field name="name">Model Name</field>
        <field name="type">ir.actions.act_window</field>
        <field name="res_model">model.to.display</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form,kanban,nt_my_addon_custom_view</field>
    </record>
    
    <menuitem name="Model Name" id="nt_my_addon_model_name" parent="nt_my_addon_root" action="nt_my_addon_tree_action" />
    
  4. FormView 为例:

    1. 在视图之间切换的时候,首先调用基类 instance.web.viewswitch_mode 方法,目的是做 lazy loading;完成后调当前类的 do_show 方法;获取数据后调用当前类的 load_record
    2. 在记录之间切换的时候,首先调用当前类的 execute_pager_action 方法,然后调用 reload,获取数据后调用 load_record
    3. 上述调用都是用的 event dispatcher,不知道为什么;可能是 Backbone 的编码风格

与后端的数据交互

  1. 官方接口提供了基于后端 ORM 的语义化查询,返回值是基于 jQueryDeferred(没细看版本,反正要重新封装)

     var _adapter = new oe_instance.web.Model('res.users');
    
     _adapter
       .query(['name', 'login', 'user_email', 'signature'])
       .filter(['active', '=', true],
               ['company_id', '=', main_company]])
       .limit(15)
       .all()
       .then(function (data) {
         //
       });
    
  2. 或者直接调用 Model 上的方法

     _adapter
       .call('change_password',
             ['oldpassword', 'newpassword'],
             { context: some_context })
       .then(function (data) {
         //
       });
    
     // 相当于后端的
     user_obj = self.pool.get('res.user')
     user_obj.change_password(cr, uid, oldpassword, newpassword, context=some_context)
    
  3. 当前的 context

     var _context = _adapter.context();
    

参考:

  1. (official) RPC Calls/High-level API/calling into OpenERP models

####do_action(action, options)

/addons/web/static/src/js/views.js line 295

  1. 假如不传参数,那么默认行为是 ir.actions.act_window_close

  2. 假如你知道要跳转到哪 个action,直接跳 id 是最准确的,得到的是 identical 的结果 oe_this.do_action('base.action_partner_form')

  3. 对于 dialog,可以在 options 里面设定 on_close 方法,没有传入值

     oe.this.do_action(action, {
         on_close: function () {
             console.assert(arguments.length === 0);
         }
     })
    

参考:

  1. (official) OpenERP Web Components - The Action Manager
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment