odoo16前端框架源码阅读——ormService.js

2023-11-09 06:28

本文主要是介绍odoo16前端框架源码阅读——ormService.js,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

odoo16前端框架源码阅读——ormService.js

路径:addons\web\static\src\core\orm_service.js

简单翻译一下代码中的注释:

ORM服务是js代码和python的ORM层通信的标准方法。

然后讲了One2many and Many2many特使的指令格式,每个指令都是3元组,其中:

第一个参数是固定的整数从0-6,代表指令本身

第二个参数:要么是0(新增记录的时候)要么是关联的记录id(其他update,delete,link,unlink情况)

第三个参数 要么是value(新增或者更新),要么是新的ids(command set) 要么是0,(删除,unlink,link,clear),

再往后,就是几个验证函数,没啥好说的

重点来了:

export class ORM {constructor(rpc, user) {this.rpc = rpc;this.user = user;this._silent = false;}get silent() {return Object.assign(Object.create(this), { _silent: true });}

这里定义并导出了ORM类, 构造函数中引用了rpc和user服务,并且还有一个私有变量_silent, 这个变量暂时不清楚干嘛的,下面这句有意思

 return Object.assign(Object.create(this), { _silent: true });

返回一个同样的orm对象,只是_silent的值编程了true。

这里提一嘴:

Object.create 是创建一个跟自己同样的对象

Object.assign(target,souce1,source2…) 是将第一个对象后面的所有对象的属性付给第一个对象(目标对象),同名的属性会覆盖。

继续往下看call方法,这个是ormService的核心函数

 call(model, method, args = [], kwargs = {}) {validateModel(model);const url = `/web/dataset/call_kw/${model}/${method}`;const fullContext = Object.assign({}, this.user.context, kwargs.context || {});const fullKwargs = Object.assign({}, kwargs, { context: fullContext });const params = {model,method,args,kwargs: fullKwargs,};return this.rpc(url, params, { silent: this._silent });}

首先验证了模型名是否合法, 是否是字符串,并且字符串长度是否等于0,这有点不太严谨啊,怎么也应该验证个.吧,

function validateModel(value) {if (typeof value !== "string" || value.length === 0) {throw new Error(`Invalid model name: ${value}`);}
}

然后就是拼凑params, 就当前用户的context, kwargs.context, kwargs 拼凑成一个对象fullKwargs,然后再跟model,method,args组成一个参数对象params

最后调用rpc, 注意,发送到的url是

 const url = `/web/dataset/call_kw/${model}/${method}`;

我们开看看这个路由都干了啥

文件路径: addons\web\controllers\dataset.py

    @http.route(['/web/dataset/call_kw', '/web/dataset/call_kw/<path:path>'], type='json', auth="user")def call_kw(self, model, method, args, kwargs, path=None):return self._call_kw(model, method, args, kwargs)

我稍微修改了一下代码,将几个参数都打印了出来

    def call_kw(self, model, method, args, kwargs, path=None):print(model)print(method)print(args)print(kwargs)print(path)return self._call_kw(model, method, args, kwargs)

打印结果:

res.users
systray_get_activities
[]
{'context': {'lang': 'zh_CN', 'tz': 'Asia/Shanghai', 'uid': 2, 'allowed_company_ids': [1]}}
res.users/systray_get_activities

这样就一目了然了,

然后调用了内部方法

 return self._call_kw(model, method, args, kwargs)
    def _call_kw(self, model, method, args, kwargs):check_method_name(method)return call_kw(request.env[model], method, args, kwargs)

第一步检查方法名,凡是以下划线开头或者init的方法都不允许远程调用。

def check_method_name(name):""" Raise an ``AccessError`` if ``name`` is a private method name. """if regex_private.match(name):raise AccessError(_('Private methods (such as %s) cannot be called remotely.', name))

然后第二步又调用了call_kw ,这个call_kw 非彼call_kw, 因为前面没有加self,事实上,这个call_kw是从api中引入的

from odoo.api import call_kw

好吧,继续跟踪,到了api中的call_kw

odoo/api.py

def call_kw(model, name, args, kwargs):""" Invoke the given method ``name`` on the recordset ``model``. """method = getattr(type(model), name)api = getattr(method, '_api', None)if api == 'model':result = _call_kw_model(method, model, args, kwargs)elif api == 'model_create':result = _call_kw_model_create(method, model, args, kwargs)else:result = _call_kw_multi(method, model, args, kwargs)model.env.flush_all()return result

跟踪到这里,有点意思了,先获取了model的方法,并获取了方法的_api属性, 这个属性是怎么来的呢, 看到api就想到了那几个装饰器,怀着碰碰运气的想法查看了一下api.py, 下面是我们熟悉的@api.model 的源码,果然是在装饰器里设置了_api这个属性

def model(method):""" Decorate a record-style method where ``self`` is a recordset, but itscontents is not relevant, only the model is. Such a method::@api.modeldef method(self, args):..."""if method.__name__ == 'create':return model_create_single(method)method._api = 'model'return method

到这里就明了了,根据不同的装饰器,调用不同的方法来处理rpc请求。

我们言归正传,继续回到ormService

call后面的函数,其实都是最终都是调用了call方法, 知识在调用之前,传递了不同的参数,这个要结合后台的python代码再去细看,这里就不展开了。

下面是ormService的最后部分:

1、在调用orm方法的时候可以设置一些选项,比如

const result = await this.orm.withOption({shadow: true}).read('res.partner', [id]);

shadow是个什么鬼?

2、终于出现了ormService的定义

它依赖于rpc和user两个服务,rpc发送http请求,而user提供当前用户的上下文环境

列举了支持的方法,这些方法在ORM类中都有定义。

start函数: 参数是env和{rpc,user} 返回了一个ORM实例。

最后注册了这个ormService服务。

/*** Note:** when we will need a way to configure a rpc (for example, to setup a "shadow"* flag, or some way of not displaying errors), we can use the following api:** this.orm = useService('orm');** ...** const result = await this.orm.withOption({shadow: true}).read('res.partner', [id]);*/
export const ormService = {dependencies: ["rpc", "user"],async: ["call","create","nameGet","read","readGroup","search","searchRead","unlink","webSearchRead","write",],start(env, { rpc, user }) {return new ORM(rpc, user);},
};registry.category("services").add("orm", ormService);

附录:ormService.js 代码

/** @odoo-module **/import { registry } from "./registry";/*** This ORM service is the standard way to interact with the ORM in python from* the javascript codebase.*/// -----------------------------------------------------------------------------
// ORM
// -----------------------------------------------------------------------------/*** One2many and Many2many fields expect a special command to manipulate the* relation they implement.** Internally, each command is a 3-elements tuple where the first element is a* mandatory integer that identifies the command, the second element is either* the related record id to apply the command on (commands update, delete,* unlink and link) either 0 (commands create, clear and set), the third* element is either the ``values`` to write on the record (commands create* and update) either the new ``ids`` list of related records (command set),* either 0 (commands delete, unlink, link, and clear).*/
export const x2ManyCommands = {// (0, virtualID | false, { values })CREATE: 0,create(virtualID, values) {delete values.id;return [x2ManyCommands.CREATE, virtualID || false, values];},// (1, id, { values })UPDATE: 1,update(id, values) {delete values.id;return [x2ManyCommands.UPDATE, id, values];},// (2, id[, _])DELETE: 2,delete(id) {return [x2ManyCommands.DELETE, id, false];},// (3, id[, _]) removes relation, but not linked record itselfFORGET: 3,forget(id) {return [x2ManyCommands.FORGET, id, false];},// (4, id[, _])LINK_TO: 4,linkTo(id) {return [x2ManyCommands.LINK_TO, id, false];},// (5[, _[, _]])DELETE_ALL: 5,deleteAll() {return [x2ManyCommands.DELETE_ALL, false, false];},// (6, _, ids) replaces all linked records with provided idsREPLACE_WITH: 6,replaceWith(ids) {return [x2ManyCommands.REPLACE_WITH, false, ids];},
};function validateModel(value) {if (typeof value !== "string" || value.length === 0) {throw new Error(`Invalid model name: ${value}`);}
}
function validatePrimitiveList(name, type, value) {if (!Array.isArray(value) || value.some((val) => typeof val !== type)) {throw new Error(`Invalid ${name} list: ${value}`);}
}
function validateObject(name, obj) {if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {throw new Error(`${name} should be an object`);}
}
function validateArray(name, array) {if (!Array.isArray(array)) {throw new Error(`${name} should be an array`);}
}export class ORM {constructor(rpc, user) {this.rpc = rpc;this.user = user;this._silent = false;}get silent() {return Object.assign(Object.create(this), { _silent: true });}call(model, method, args = [], kwargs = {}) {validateModel(model);const url = `/web/dataset/call_kw/${model}/${method}`;const fullContext = Object.assign({}, this.user.context, kwargs.context || {});const fullKwargs = Object.assign({}, kwargs, { context: fullContext });const params = {model,method,args,kwargs: fullKwargs,};return this.rpc(url, params, { silent: this._silent });}create(model, records, kwargs = {}) {validateArray("records", records);for (const record of records) {validateObject("record", record);}return this.call(model, "create", records, kwargs);}nameGet(model, ids, kwargs = {}) {validatePrimitiveList("ids", "number", ids);if (!ids.length) {return Promise.resolve([]);}return this.call(model, "name_get", [ids], kwargs);}read(model, ids, fields, kwargs = {}) {validatePrimitiveList("ids", "number", ids);if (fields) {validatePrimitiveList("fields", "string", fields);}if (!ids.length) {return Promise.resolve([]);}return this.call(model, "read", [ids, fields], kwargs);}readGroup(model, domain, fields, groupby, kwargs = {}) {validateArray("domain", domain);validatePrimitiveList("fields", "string", fields);validatePrimitiveList("groupby", "string", groupby);return this.call(model, "read_group", [], { ...kwargs, domain, fields, groupby });}search(model, domain, kwargs = {}) {validateArray("domain", domain);return this.call(model, "search", [domain], kwargs);}searchRead(model, domain, fields, kwargs = {}) {validateArray("domain", domain);if (fields) {validatePrimitiveList("fields", "string", fields);}return this.call(model, "search_read", [], { ...kwargs, domain, fields });}searchCount(model, domain, kwargs = {}) {validateArray("domain", domain);return this.call(model, "search_count", [domain], kwargs);}unlink(model, ids, kwargs = {}) {validatePrimitiveList("ids", "number", ids);if (!ids.length) {return true;}return this.call(model, "unlink", [ids], kwargs);}webReadGroup(model, domain, fields, groupby, kwargs = {}) {validateArray("domain", domain);validatePrimitiveList("fields", "string", fields);validatePrimitiveList("groupby", "string", groupby);return this.call(model, "web_read_group", [], {...kwargs,groupby,domain,fields,});}webSearchRead(model, domain, fields, kwargs = {}) {validateArray("domain", domain);validatePrimitiveList("fields", "string", fields);return this.call(model, "web_search_read", [], { ...kwargs, domain, fields });}write(model, ids, data, kwargs = {}) {validatePrimitiveList("ids", "number", ids);validateObject("data", data);return this.call(model, "write", [ids, data], kwargs);}
}/*** Note:** when we will need a way to configure a rpc (for example, to setup a "shadow"* flag, or some way of not displaying errors), we can use the following api:** this.orm = useService('orm');** ...** const result = await this.orm.withOption({shadow: true}).read('res.partner', [id]);*/
export const ormService = {dependencies: ["rpc", "user"],async: ["call","create","nameGet","read","readGroup","search","searchRead","unlink","webSearchRead","write",],start(env, { rpc, user }) {return new ORM(rpc, user);},
};registry.category("services").add("orm", ormService);

这篇关于odoo16前端框架源码阅读——ormService.js的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/374441

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能