Odoo 的 jsonrpc 接口及参数解释

Odoo 的 WEB 前端是完全通过接口和后端通讯的,几乎实现了前后端分离。主要通过 JSONRPC。登录成功后就可以向 Odoo 后台发送 JSONRPC了。

以下几个接口是经常使用的:

1)model 搜索

接口地址:web/dataset/search_read
接口参数:JSRONRPC 参数,下面描述
接口作用:所有列表视图或者 kanban 视图都需要调用的接口,支持参数 offset 和 limit,还有 sort,就是可以用来分页获取数据表的中的记录。如 offset 10, limit 20,就是从第10条记录开始获取 20 条。当然它也返回总的满足搜索条件的记录数量,这样调用者才知道要分多少个页才能把数据都捞出来。

如果用这些接口来开发原生的 Android 应用,

    @Headers({"Content-Type: application/json","Accept: application/json"})
    @POST("web/dataset/search_read")
    Call<String> postSearchRead(@Body RequestBody body);

上文代码是使用 Android 中 Retrofit 对 HTTP 接口的一个描述。

参数一般形如:

{
  jsonrpc: "2.0",
  method: "call",
  id:  651978786,
  params:  {
      model: ""
      domain: [[]]
      fields: ["id", "name", …]
      limit: 80
      sort: ""
      context: {
          lang: "zh_CN",
          tz: false,
          uid: 2,
          allowed_company_ids: [1]
         }
    }
}

jsonrpc, method, id 这三个字段是必须提供的,其中 id 的值一直在变大,用秒表示就可以,因为这个id 表示这个请求的唯一性,没有任何其它含义,Odoo 后台若有任何结果返回都会有这个相同的 id,表示这个返回结果对应的哪个请求。

params 中主要包含 model,是表名称,domain 是搜索条件,fields 是要返回的数据字段名称列表,如果没有指定就返回所有字段,limit 是限制返回记录条数,offset 没有就是0,context 也是一个通用结构,这里面往往含有用户的 session 数据。

请求的结果:

{"jsonrpc": "2.0", "id": 651978786, "result": {"length": 85, "records": [{}, {}]}}

length 是所有满足搜索条件的记录数量,不是这次返回了多少个,返回多少个需要通过看 records 数组的长度,records 数组里面的每一项就是一个记录。

2)数据记录的 读、写、创建

接口名称:call kw (以 KEY WORD 方式调用)
接口地址:web/dataset/call_kw/{model}/{method}
接口参数:下面描述
接口作用:call kw 这个接口有一定的泛化性,可以支持很多操作,CRUD 都需要使用这个接口,自己写一个 model 中的 method 接口也是需要使用这个接口。其中 model 和 method 是补全这个调用 URL 必须提供的,如读一个表叫做 test_table 的记录就需要调用 web/dataset/call_kw/test_table/read。具体的记录 id 要在参数中提供。

基本的参数的结构:

{
  jsonrpc: "2.0",
  method: "call",
  id:  651978786,
  params:  {
    args: [[id], ["id", "name"]],
    kwargs:{
      context: {}
    },
    method: "read",
    model:"test_table"
  }
}

params 中 method 和 model 和 url 中的一样,分别是要调用的方法名和表名称。 args 是个数组,第一个元素是记录 id,第二个是字段名称列表。

kwargs 只有一个参数,即上文提到的 context ,含有用户 session 的一些内容。

返回结果:

{"jsonrpc": "2.0", "id": 268487428, "result": [{"id": 1305,  "name":""}]

如果某个field 没有值,就是 false。

同样 method 可以是 read/create/write 基本差不多,read 和 write 要提供 id,而create 没有 id。

3)upload attachment

接口名称:upload attachment
接口地址:web/binary/upload_attachment
接口参数:表单 (不再是 JSON)
接口作用:可以上传 附件文件,返回文件 id,这个 id 可以用在数据表之中

    @POST("web/binary/upload_attachment")
    Call<String> postUploadAttachment(@Body MultipartBody body);

上面是 Retrofit 的接口描述,body 需要再构造:

      // upload then return the attachment id, ticket db table store the id
        MultipartBody.Builder b = new MultipartBody.Builder();
        MultipartBody body = b.setType(MultipartBody.FORM)
                .addFormDataPart("csrf_token", "csrf_token...")
                .addFormDataPart("callback", "any-upload-id")
                .addFormDataPart("model", "model")
                .addFormDataPart("id", "0")
                .addFormDataPart("ufile", localFileName(),
                        RequestBody.create(MediaType.parse("image/jpeg"), localFile))
                .build();

上述代码能够在 Android Java 中构造一个上传文件的请求。可以看出来是要求提供一个 multipart form 的表单,必须提供 csrf token;callback 是个字符串,实际上是一个页面元素的 id,在系统返回值中使用了这个 id ; model 就是表名称,说明这个附件是为哪个表准备的;id 一般来说应给是记录 id,但是没用上,传 0 即可,因为附件属于哪个表的哪个记录由记录本身维护一个列表决定;ufile 必须是这个名字,来代表文件。所有参数名称必须严格一致,因为后台是需要跟 Python 函数参数映射的,名字对不上必然出错。

上传成功后会返回一段 Javascript 代码,这段代码。

<script language="javascript" type="text/javascript">
                    var win = window.top.window;
                    win.jQuery(win).trigger("oe_fileupload_temp396", [{"filename": "badge.png", "mimetype": "image/png", "id": 750, "size": 6302263}]);
</script>

通过查看 Odoo 的页面构造代码:

<t t-name="HiddenInputFile">
    <div t-attf-class="o_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style" aria-atomic="true">
        <form class="o_form_binary_form" t-att-target="fileupload_id"
              method="post" enctype="multipart/form-data" t-att-action="fileupload_action || '/web/binary/upload'">
            <input type="hidden" name="csrf_token" t-att-value="csrf_token"/>
            <input type="hidden" name="callback" t-att-value="fileupload_id"/>
            <input type="file" class="o_input_file" name="ufile"
                t-att="{'multiple': multi_upload ? 'multiple' : null, 'accept': widget.accepted_file_extensions || '*'}"/>
            <t t-raw="0"/>
        </form>
        <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/>
    </div>
</t>

可以看出来这个表单提交给一个 iframe,使用了一个提交表单页面无刷新的技巧。通过指定的 iframe 打开返回的结果,返回结果的 JS 就会在 iframe 内执行。trigger 了一个event,event 的名字就是那个 callback id。这里面 iframe 也使用了这个 id,但是和 trigger 的参数意图不一样,iframe 是为了让 form 指定 target,而 trigger 是一个事件的名称。虽然都使用了一个值,不同的作用。

在 Odoo JS 的代码中:

        $(window).on(this.fileuploadID, this._onFileLoaded.bind(this));

就是对 以 callback id 命名的 event 进行监听。这样当文件上传成功后,前端代码能够知道。

4)下载图片附件文件

通过3)上传的图片文件是可以通过一个 URL GET 直接引用的 基本规则为

http://ppmessage.cn/web/image/file_id/200x160

file id 就是upload 返回的 id,200x160 可以随便写,Odoo 负责转。

在 Android 中可以通过 Glide 加载 URL 到界面元素之中:

            GlideUrl cookie = new GlideUrl(url, new LazyHeaders.Builder().addHeader("Cookie", session_id).build());
            Glide.with(itemView)
                    .load(cookie)
                    .centerCrop()
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .skipMemoryCache(true)
                    .into(someImageView);

其中 Cookie 的 session id 必须提供,不然通过不了 Odoo 的安全验证。

5)设置头像

设置头像和上传附件不太一样,头像确实是存储在数据库之中,而不是放在文件里面的。所以就通过 res_users 的write 接口就可以更新头像,对于 Odoo 13,头像的 field 是 image_1920。

读头像的 URL 规则:

http://ppmessage.com/web/image?model=res.users&id=uid&field=image_128&unique=timestamp;

uid 就是用户 id,field 有几个可以选择 image_128 是其中之一,unique 是时间构成一个字符串,如 20191231150028。

再用 Glide load 一下就能加载了。

上述接口和运转方式都是通过 Odoo 13 测试的,并且这些都不是 Odoo 官方文档描述的,可能随时都在变。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266