odoo的阶段性总结:服务器的启动、模块的加载、请求处理、页面生成

odoo的阶段性总结:服务器的启动、模块的加载、请求处理、页面生成

经过漫长的阅读代码,搞清了启动的过程。先简单做个总结。如有遗漏之后再做补充。

系统的启动,模块的加载

结合之前研究过的registry,系统启动时会发生如下动作:

  • 首先加载全局模块webweb_kanban。(在没有确定数据库地址之前,只能显示数据库选择页面。所以这个时候只需要这两个模块就可以了)
    • import controller时,由于元类的作用,controller类会自动加载到解释器中
  • 根据配置创建web服务器(线程的、进程的),所有的服务器都使用odoo.service.wsgi_server.application来处理请求。
    • 具体处理请求的是odoo.http.Root
      • 根据需要看要不要再次加载插件
      • 只有当首次接收请求的时候,才会执行加载插件
  • 启动服务器
    • 在启动web服务器之前,首先创建registry。(在进程的实现中,registry会在进程fork之前创建,fork之后registry会被拷贝到各个进程的内存空间中)
    • 当数据库选定之后,registry会根据配置去加载模块
      • 先加载base模块
        • 先创建base模块的依赖关系图graph
        • 使用graph加载base
          • 获取模块中的所有模型
          • 组装配置模型类
            • 根据模型的属性,创建新的模型类,并将模型类注册到registry
            • 根据模型的属性,为新的模型类添加字段,关联关系等
          • 初始化模型
            • 根据模型类,创建模型类对应的表
          • 装载定义在__manifast__.py中的模块的数据
            • 获取文件列表
            • 调用odoo.tools.convert.convert_file装载文件。
              • 判断文件类型,根据文件类型使用不同的方法解析
              • 根据情况将数据写入ir.model.data
              • 根据情况将数据写入模型自己的数据表中
      • 根据配置标注其它需要加载的模块
      • 根据标注加载模块
        • 使用graph进行模块的记载(下边以web模块为例,看一下模块数据的加载)
          • 获取数据文件:views/webclient_templates.xml
          • 调用odoo.tools.convert.convert_file装载文件
            • 创建xml专用的解析器对象xml_import
            • 解析xml文件
              • 遍历整个xml文档树,根据节点的类型调用不同的函数来进行处理。具体到views/webclient_templates.xml,这个文件由template组成,对应的函数是_tag_template。这个函数在结尾调用_tag_record,这个函数会将数据文件里的内容写入ir_model_data表中。
    • 模块加载完毕后,服务器开始运行。等待处理请求。

请求处理

  • 首次接受到请求
    • odoo.http.Root根据请求创建JsonRequest或者HttpRequest
    • Registry中查找ir.http模型
    • 使用ir.http模型_dispatch转发请求
      • _dispatch查询路由表
        • 如果路由表不存在,使用odoo.http.routing_map创建路由表
        • 使用路由表获取处理请求的具体controller
        • 校验用户
        • controller放入请求对象中
        • 调用request.dispatch处理请求
    • request.dispatch函数中(以HttpRequest为例),对请求参数、请求头、等进行校验,然后通过调用_call_function来调用之前放入requestcontroller
    • 如果controller中的处理函数设置了延迟生成页面,则在dispatch结束的时候生成页面。

页面生成

请求处理完成后,要生成显示的页面。以入口地址为例:

  • 处理/请求的controlleraddon.web.controllers.main.Home,定义在web模块中,处理方式是直接跳转到/web
  • 处理/web请求的controller同上,使用web.webclient_bootstrap这个模版来生成页面。
class Home(http.Controller):
    @http.route('/', type='http', auth="none")
    def index(self, s_action=None, db=None, **kw):
        return http.local_redirect('/web', query=request.params, keep_hash=True)
        
    @http.route('/web', type='http', auth="none")
    def web_client(self, s_action=None, **kw):
        ensure_db()
        if not request.session.uid:
            return werkzeug.utils.redirect('/web/login', 303)
        if kw.get('redirect'):
            return werkzeug.utils.redirect(kw.get('redirect'), 303)

        request.uid = request.session.uid
        context = request.env['ir.http'].webclient_rendering_context()

        return request.render('web.webclient_bootstrap', qcontext=context)
  • 请求的处理,之前已经提到过了。request.render函数里,会创建Response对象。不管是否使用延迟创建页面,最终是通过response.render来生成页面。
  • response.render会用ir.ui.view模型来根据模版来创建页面。具体到/web请求的处理,就是使用ir.ui.view模型的render_template函数,利用web.webclient_bootstrap模版来创建页面。
    • 先用ir.ui.view模型的get_view_id函数中,先在ir.model.data模型中查找模版:根据模版的名字,获取res_id select ir_model_data.id from ir_model_data where module='web' and name='webclient_bootstrap'
    • 使用基类的browse函数,创建一个View对象,保存res_id
    • 调用Viewrender函数
      • 调用ir.qwebrender函数
        • 调用QWebrender函数
          • 使用ir.qwebload读入模版
            • 使用ir.ui.view中的read_template读入模版
              • ir_ui_view表中获取arch_fs信息,然后读取对应的文件作为arch
          • 调用ast对模版进行处理,生成一个python函数
          • 用生成的函数生成最终的页面

通过访问/web只是获得了页面的框架、样式表、js等静态资源。然后odoo会通过自己实现的一套js框架,从服务器获取展示所需的其它部分绘制到页面上。

接下来的部分基于对前端的一些猜测: 当浏览器打开由模版生成的页面后,将页面上相关的静态资源下载到本地。其中的js加载到浏览器后,会向服务器发起请求,来获取当前页面上要的元素:

  • /web/action/load
    获取要展示模块的行为
    json请求:

    {jsonrpc:"2.0",method="call",params:{action_id:261}}
    

    服务器会从ir_action中查询action_id对应的记录,并返回action相关的信息

    { jsonrpc:"2.0",
    result:{ ...
        xml_id:"qingjia.action_qingjia_qingjd",
        ...
        res_model:"qingjiaj.qingjd",
        search_view:"{'name':'default','arch':....}",
        ..
        }
    }
    

    odoo的js框架会根据收到的数据,在页面上添加响应的元素(如按钮)

  • /web/dataset/call_kw/qingjia.qingjd/load_views
    加载模块展示需要用到的view json请求:

    {jsonrpc:"2.0", method:"call", params:{
    model:"qingjia.qingjd",
    method:"load_views",
    kwargs:{
    views:[[null, "list"],[null,"from"],[false,"search"]],
    ...
    }
    }}
    

    服务器会服务器返回结果:

    {jsonrpc:"2.0"
         result:{
             fields:{
                 id:{...},
                 create_date:{...},
                 ...
             },
             fields_views:{
                 form:{},
                 list:{},
                 search:{},
             },
             ...
         }
    }
    

    可以看到在一次请求中,获得了3种view。这些结果会缓存在浏览器,odoo的js框架之后就不需要反复的去获取view。同时js框架会根据以上内容在页面上生成相应的内容。

……

 
comments powered by Disqus