最好用的免費ERP系統Odoo 12開發手冊 | 第九篇 外部API 整合第三方系統
阿新 • • 發佈:2020-08-01
第九章 外部API-整合第三方系統
1. Odoo伺服器端帶有外部API,可供網頁客戶端和其他客戶端應用使用. (所以Odoo作為一個後臺框架的功能就體現了)
2. 本文中我們將學習如何在我們的客戶端程式中使用Odoo的外部API.
3. 為了避免引入大家所不熟悉的語言,此處我們將使用基於Python的客戶端,但這種RPC呼叫的處理方法也適用於其他程式語言.
4. 我們一起了解如何使用 Odoo RPC呼叫,然後根據所學知識,使用Python建立一個簡單的圖書命令列應用.
使用 XML-RPC 連線 Odoo API
1. 訪問服務的最簡單方法是使用XML-RPC,我們可以使用Python標準庫的xmlrpclib來實現. 2. 不要忘記我們是在編寫客戶端程式連線服務端,因此需執行Odoo服務端例項來供連線. 3. 本例中我們假設Odoo服務端例項在同一臺機器上執行,但你可以使用任意執行服務的其他機器,只要能連線其IP地址或伺服器名. 4. 讓我們來初次連線Oodo外部API. 開啟Ipython或者 jupyter lab輸入以下程式碼: # 使用Python標準庫中得 xmlrpclib 來實現 XML-RPC from xmlrpc import client srv = "http://localhost:8269" common = client.ServerProxy('%s/xmlrpc/2/common' % srv) common.version() 執行結果: {'server_serie': '12.0', 'server_version': '12.0-20191120', 'protocol_version': 1, 'server_version_info': [12, 0, 0, 'final', 0, '']} (1) 這裡我們匯入了 xmlrpc.client庫,然後建立了一個包含服務地址和監聽埠資訊的變數. (2) 訪問服務端公共服務(無需登入),在終端 /xmlrpc/2/common 上暴露. (3) 其中一個可用的方法是 version(),用於檢視服務端版本. 我們使用它確認可與服務端進行通訊. 5. 另外一個公共方法是 authenticate(). (1) 你可能以為它會建立會話,但實際上不會. (2) 該方法僅僅確認使用者名稱和密碼可被接受,請求不使用使用者名稱而是它返回的使用者ID. 示例如下: # authenticate() 是另外一個公共方法 db = "MyERP12" # 這裡是用賬戶的使用者名稱和密碼,不是資料庫的 user,pwd="admin","123456" uid = common.authenticate(db,user,pwd,{}) print(uid) 執行結果: 2 (1) 首先建立變數db,來儲存使用中的資料庫名. (2) 如果登入資訊錯誤,不會返回使用者ID,而是返回False值.(是使用者名稱賬號和密碼) (3) authenticate() 最後一個引數是使用者代理(User Agent)環境,用於提供客戶端一些元資料(metadata) 它是必填的,但可以是一個空字典.
使用 XML-RPC 執行伺服器端方法
1. 使用XML-RPC, 不會維護任何會話,每次請求都會發送驗證資訊. 這讓協議過重,但是使用簡單. 2. 下一步我們設定訪問需登入才能訪問的服務端方法. 暴露的終端地址為 /xmlrpc/2/object,示例如下: api = client.ServerProxy('%s/xmlrpc/2/object' % srv) api.execute_kw(db, uid, pwd, 'res.partner', 'search_count', [[]]) 執行結果: 43 (1) 此處我們第一次訪問服務端API,執行了Partner記錄的計數. 通過 execute_kw() 來呼叫方法,接受如下引數: excute_kw(連線的資料庫名,連線使用者ID,使用者密碼,目標模型識別符號名稱,呼叫的方法,位置引數列表,可選關鍵字引數) (2) 上面的例子中對 res.partner 模型呼叫了 search_count 方法,進一個位置引數[],沒有關鍵字引數. 該位置引數是一個搜尋域,因為我們傳入是一個空列表,它對所有 partner 進行計數. 3. 常用的操作有 搜尋search 和 讀取read. (1) 在使用RPC呼叫時,search()返回一個區塊的ID列表. (2) browse() 不可用於RPC,而應使用 read() 來得到記錄ID列表並獲取相應資料. 示例如下: domain = [('login','=','demo')] # 這裡搜尋的是登入使用者的ID 搜尋的是 res.users 這個表 api.execute_kw(db, uid, pwd, 'res.users', 'search', [domain]) 執行結果: [6] # 這裡使用 read 方法讀取指定欄位 api.execute_kw(db, uid, pwd, 'res.users', 'read', [[6]],{'fields':["login","state","email","lang"]}) 執行結果: [{'id': 6, 'lang': 'zh_CN', 'login': 'demo', 'email': '[email protected]', 'state': 'new'}] (1) 這裡查詢了demo使用者,並且通過第二行的程式碼,返回使用者的位置資訊為[6]. (2) 然後第4行使用 read 方法,將位置引數傳入,並且在最後使用欄位傳遞我們需要的欄位資訊. 4. 我們是通過兩個步驟才檢視到了demo使用者的相關欄位的資訊,需要 search 和 read 方法相配合. (1) search 和 read 相配合這樣的需求非常多 (2) Odoo提供了將這個兩個方法融合到一起的方法: search_read(), 示例程式碼: # 上面需要 search() 和 read() 兩個方法相互配合 # search_read() 將這兩個方法合二為一 api.execute_kw(db, uid, pwd, 'res.users', 'search_read', [domain],{'fields':["login","state","email","lang"]}) (1) search_read 和 read 相似,但需要 domain 代替 id列表 來作為第一個位置引數. (2) 需要說明在 read 和 search_read 中 fields 引數並非必須的. 1) 如果不傳入,則獲取所有欄位,這可能會帶來對函式欄位的大量計算,並獲得大量可能用不到的資料. 2) 通常建議明確傳入欄位列表
搜尋和讀取API方法
1. 一些模型方法可用於更具體的操作,如: (1) read([field]) 類似於 browse方法,但返回的不是記錄集,而是包含按引數指定的欄位的各行資料列表. 每一行都是一個字典,他提供可供RPC協議使用的資料的序列化展示,設計用於客戶端程式中而非服務端邏輯中. (2) search_read 在讀取結果記錄列表之後執行搜尋操作. 設計用於RPC客戶端,避免了反覆進行讀取結果和搜尋的操作. 2. 所有其他模型方法都對RPC暴露,但是以下劃線開頭的除外(_是protected,__是private) 也就是說我們可以像下面這樣使用 create,write,和unlike 修改服務端資料: # 新建立一個記錄 x = api.execute_kw(db,uid,pwd,'res.partner','create',[{"name":'Packt Pub'}]) print(x) 執行結果: 63 # 更改ID為63的名字 api.execute_kw(db,uid,pwd,'res.partner','write',[[x],{'name':'Packt Publishing'}]) 執行結果: True # 讀取相關記錄 api.execute_kw(db,uid,pwd,'res.partner','read',[[x],['id','name']]) 執行結果: [{'id': 63, 'name': 'Packt Publishing'}] 3. XML-RPC的一個缺陷是它不支援None值. (1) 有一個XML-RPC擴充套件可以支援None值,但這取決於我們客戶端說依賴的具體XML-RPC庫. (2) 不放回任何值的方法不能在XML-RPC中使用,因為預設返回的是None. 這也是為什麼方法在結束時至少帶有一個return True 語句. (3) 另外一個方案是使用Odoo同時支援的JSON-RPC. 4. 應反覆強調Odoo的外部API可在大部分程式語言中使用.(這裡使用Python是為了方便) (1) 官方文件中我們可以看到Ruby,PHP和Java實際示例. (2) 常見的示例是先使用Django或Flask連線到Odoo,然後外部前端在再來連線Django和Flask
圖書客戶端XML-RPC介面(封裝外部操作)
1. 下面就來實現圖客戶端應用.
(1) 我們將使用兩個檔案: 一個處理服務端的介面: library_api.py
(2) 另一個處理應用的使用者介面: library.py
(3) 然後我們會使用現有的OdooRPC庫來提供一個替代的實現方法.
2. 我們將建立類來配置與Odoo服務端的連線,以及 讀取/寫入圖書資料. 這將暴露基本的增刪改查方法:
(1) search_read() 獲取圖書資料
(2) create() 建立圖書
(3) write() 更新圖書
(4) unlink() 刪除圖書
3. 選擇一個目錄來放置應用檔案並建立 library_api.py 檔案. 首先新增類的構造方法,程式碼如下:
from xmlrpc import client
class LibraryAPI():
def __init__(self, srv, port, db, user, pwd):
common = client.ServerProxy(
'http://%s:%d/xmlrpc/2/common' % (srv, port))
self.api = client.ServerProxy(
'http://%s:%d/xmlrpc/2/object' % (srv, port))
self.uid = common.authenticate(db, user, pwd, {})
self.pwd = pwd
self.db = db
self.model = 'library.book'
def execute(self, method, arg_list, kwarg_dict=None):
return self.api.execute_kw(
self.db, self.uid, self.pwd, self.model,
method, arg_list, kwarg_dict or {})
def search_read(self, text=None):
domain = [('name', 'ilike', text)] if text else []
fields = ['id', 'name']
return self.execute('search_read', [domain, fields])
def create(self, title):
vals = {'name': title}
return self.execute('create', [vals])
def write(self, title, id):
vals = {'name': title}
return self.execute('write', [[id], vals])
def unlink(self, id):
return self.execute('unlink', [[id]])
測試程式碼:
# 執行一段測試程式碼
srv, db, port = 'localhost', 'MyERP12', 8269
user, pwd = 'admin', '123456'
api = LibraryAPI(srv, port, db, user, pwd)
from pprint import pprint
pprint(api.search_read())
使用OdooRPC庫
1. 另外一個可以考慮的客戶端是OdooRPC.
(1) 它是一個更流行的客戶端庫,使用JSON-RPC協議而不是XML-RPC.
(2) 事實上Odoo官方客戶端使用的就是JSON-RPC,XML-RPC更多是用於支援先後相容性.
(舊時很多程式都是使用XML,現在基本都是使用JSON)
2. OdooRPC庫可以通過PyPI安裝:
pip install odoorpc
3. 不管是使用JSON-RPC還是XML-RPC,Odoo API的使用方法並沒有什麼分別.
我們可以看到一些細節可能有區別,但是客戶端的使用方式並沒有什麼分別.
4.
(1) OdooRPC庫建立新的odoorpc.ODOO物件時建立服務端連線,然後應使用ODOO.login()來建立使用者會話.
注意: XML-RPC是不會建立會話的,但是這個 OdooRPC是會建立會話的
(2) 和服務端相似,會話有一個帶有會話環境的env屬性,包含 ID-uid 和 上下文.
5. 我們使用OdooRPC來提供實現對 libray_app 的介面,建立 library_odoorpc.py,新增如下程式碼:
from odoorpc import ODOO
class LibraryAPI():
def __init__(self, srv, port, db, user, pwd):
self.api = ODOO(srv, port=port)
self.api.login(db, user, pwd)
self.uid = self.api.env.uid
self.model = 'library.book'
self.Model = self.api.env[self.model]
def execute(self, method, arg_list, kwarg_dict=None):
return self.api.execute(
self.model,
method, *arg_list, **kwarg_dict)
(1) OdooRPC庫實現了Model 和 Recordset 物件來模擬服務端對應的功能.
目標是實現客戶端程式設計與服務端程式設計基本一致.
(2) 客戶端使用的方法將通過儲存在 self.Model 屬性中的圖書模型來利用這點.
(3) 這裡實現的execute() 方法並會在我們客戶端中使用,僅用於本文中討論的其他實現進行對比.
4. 下面我們來實現 search_read(),create(),write()和unlink()這些客戶端方法.
def search_read(self, text=None):
domain = [('name', 'ilike', text)] if text else []
fields = ['id', 'name']
return self.Model.search_read(domain, fields)
def create(self, title):
vals = {'name': title}
return self.Model.create(vals)
def write(self, title, id):
vals = {'name': title}
self.Model.write(id, vals)
def unlink(self, id):
return self.Model.unlink(id)
(1) 注意這段程式碼和Odoo服務端程式碼相似,因為它使用了與Odoo中外掛寫法相近的API.
(2) 再次執行測試程式碼,執行效果應該和之前的一致.
瞭解ERPpeek客戶端
1. ERPpeek是一個多功能工具,即可以作為互動式命令列介面(CLI)也可以作為Python庫,它提供了xmlrpc庫更便捷的API.
2. 通過pip進行安裝: pip install erppeek
...
總結
1. 本文的目標是學習外部API如何運作以及它們能做些什麼.
(1) 一開始我們通過一個簡單的Python XML-RPC客戶端來進行探討.
(2) 但外部API可用於其他語言,事實上官方文件中包含了Java,PHP和Ruby的程式碼示例.
2. 有很多庫可處理 XML-RPC或JSON-RPC,有些是通用的,有些僅適用於Odoo.我們使用了一個指定庫OdooRPC.
3. 以上給我們就總結了本文有關程式設計API和業務邏輯的學習.