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 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 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 代码,这段代码。
通过查看 Odoo 的页面构造代码:
可以看出来这个表单提交给一个 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 官方文档描述的,可能随时都在变。