第十九章 服务混合类

服务混合类作为WC中基础服务提供类,提供了很多全局基础功能。

我们前面提到的模型类也是继承自服务混合类,因此本章所介绍的功能在模型及其子模型中都可以直接使用。

var Model = Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
    ...
}

RPC服务

服务混合类提供的重要功能之一就是为模型提供RPC接口,模型可以直接使用_rpc方法调用odoo的RPC服务接口。

    var query = rpc.buildQuery(params);
    var prom = this.call('ajax', 'rpc', query.route, query.params, options, this);
    if (!prom) {
        prom = new Promise(function () {});
        prom.abort = function () {};
    }
    var abort = prom.abort ? prom.abort : prom.reject;
    if (!abort) {
        throw new Error("a rpc promise should always have a reject function");
    }
    prom.abort = abort.bind(prom);
    return prom;
},

RPC作为WC的核心功能之一,这里有必要进行一个详细的介绍。

RPC服务不是一个Odoo类,它只是一个仅仅包含了两个函数的集合对象。

RPC服务包含两个函数:

  • query: query函数用来执行一个RPC调用,内部实现使用了buildQuery函数。
  • buildQuery: 构建RPC调用的路由和参数函数。

RPC细节

想要了解RPC如何使用,那么我们就必须清楚,WC是如何发起一个RPC请求的。

我们知道,我们在浏览器的中所有明确了模型和方法的数据操作都是由JsonRPC代为封装请求,其路由格式为 /web/dataset/call_kw/{model_name}/{method_name}。然后再将方法的参数赋值到参数args,将上下文对象context,模型model和方法名method赋值到kwargs参数中,然后发起Http请求。

而rpc服务的核心就是将用户指定的参数,组装成满足要求的数据,然后发起调用。RPC服务依赖于Ajax服务, 实际的调用由Ajax服务完成。

RPC服务的buildQuery方法用来格式化请求数据,query方法使用Ajax服务完成调用。buildQuery内部针对请求的数据格式不同做了不同的逻辑判断:

  • 如果options中包含route那么直接使用options中的route
  • 否则检查options中是否指定了model和method,如果是,那么设定请求路由为:/web/dataset/call_kw/{model_name}/{method_name}
  • 如果options中包含method,那么设置params参数中的各种参数与method匹配
  • 如果method是read_group或web_read_group(分组),那么在请求中设置groupby等参数。
  • 如果method是search_read,那么设置参数中的kwargs与options中的参数匹配。
  • 如果options的route是/web/dataset/search_read,设置options中的参数与params中的参数匹配。

我们来看一个直接使用rpc服务的示例:

var rpc = require("web.rpc");
rpc.query({
    "model": "ir.config_parameter",
    "method": "get_param",
    "args": ["abc"]
}).then((result) => {
    if (result) {
        ... //do the right ting.
    }
});

根据我们之前的学习,query方法的第一个参数即使不用明确指明route和params参数,系统也能够根据params中的数据正确地配置组织相应的数据,也就是说,上面的写法等同于:

var rpc = require("web.rpc");
rpc.query({
    route: '/web/dataaset/call_kw/ir.config_parameter/get_param',
    params: {
        "model": "ir.config_parameter",
        "method": "get_param",
        "args": ["abc"]
    }
}).then((result) => {
    if (result) {
        ... //do the right ting.
    }
});

执行动作

服务混合类提供的另外一个重要的功能即提供了直接调用后台动作(ir.actions)的方法。

do_action: function (action, options) {
    var self = this;
    return new Promise(function (resolve, reject) {
        self.trigger_up('do_action', {
            action: action,
            options: options,
            on_success: resolve,
            on_fail: reject,
        });
    });
},

从do_action的定义中可以看出来,其原理是用了trigger_up方法调用了do_action事件。

flags

在ir.actions.act_window中存在一个特殊的字段flags, 该字段并不是一个真实的字段, 只是用来在后端配置传递参数到前端页面的一个控制变量.

这点我们可以从flags在WC中的定义可以看出:

function _getActionInfo(action, props) {
    return {
        props: Object.assign({}, props, { action, actionId: action.id }),
        config: {
            actionId: action.id,
            actionType: action.type,
            actionFlags: action.flags,
            displayName: action.display_name || action.name || "",
            views: action.views,
        },
    };
}

WC将后台传递过来的flags参数赋值给action的actionFlags属性, 然后在渲染视图的时候将actionFlags属性传递给前端视图的构造函数, 从而达到控制视图的目的.

 this.viewParams = Object.assign({}, actionFlags, {
    action,
    // legacy views automatically add the last part of the breadcrumbs
    breadcrumbs: breadcrumbsToLegacy(breadcrumbs),
    modelName: this.props.resModel,
    currentId: this.props.resId,
    controllerState: {
        currentId:
            "resId" in this.props
                ? this.props.resId
                : this.props.state && this.props.state.currentId,
        resIds: this.props.resIds || resIds,
        searchModel,
        searchPanel,
    },
});

可以将flags理解为WC给后台留的一个隐秘的"武器", 这个"武器"并没有在后端的"武器库"中注册,但是却可以在返回动作中实现对前台视图元素的控制.

这里简单举一个案例用法, 笔者在给上海某BPM公司开发任务管理系统时碰到了下面一个需求, 要求用户在单机列表视图中的查看任务按钮时,弹出的FORM窗口不能显示创建和取消按钮.

很显然,我们需要在按钮的返回动作中返回一个FORM窗体, 但是问题在于如何控制创建和取消按钮的显示, 答案就是使用flags参数,将defaultButtons参数设置为False

return {
    "type": "ir.actions.act_window",
    "name": "任务反馈汇总",
    "view_mode":"form",
    "res_model":"k2.worklog.wizard",
    "res_id": wizard.id,
    "views":[(self.env.ref("k2_project.view_worklogs_wizard_form").id,"form")],
    "flags":{
        "form":{
            "default_buttons": False
        }
    },
    "target":"new"
}

results matching ""

    No results matching ""