今天項目已經可以作一個簡單的後端服務了,在mysql中新建一個表,就能自動提供restful api的CURD服務了。node
實現了以下形式的rest-apimysql
[GET]/rs/users/{id} [GET]/rs/users/key1/value1/key2/value2/.../keyn/valuen [POST]/rs/users [PUT]/rs/users/{id} [DELETE]/rs/users/{id}
該類實現與pymysql庫的對接,提供標準CURD接口。git
在數據庫對應創建users表,腳本以下:github
CREATE TABLE `users` ( `_id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '標題名稱', `phone` varchar(1024) DEFAULT '', `address` varchar(1024) DEFAULT NULL, `status` tinyint(4) DEFAULT '1' COMMENT '狀態:0-禁;1-有效;9刪除', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間', PRIMARY KEY (`_id`), UNIQUE KEY `uuid` (`_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表';
數據鏈接配置,不入版本庫。sql
{ "db_config": { "db_host": "ip", "db_port": 1234, "db_username": "root", "db_password": "******", "db_database": "name", "db_charset": "utf8mb4" } }
用函數exec_sql封裝pymysql,提供統一訪問mysql的接口。is_query函數用來區分是查詢(R)仍是執行(CUD)操做。出錯處理折騰了很久,插入異常返回的錯誤形式與其它的居然不同!返回參數是一個三元組(執行是否成功,查詢結果或錯誤對象,查詢結果數或受影響的行數)數據庫
with open("./configs.json", 'r', encoding='utf-8') as json_file: dbconf = json.load(json_file)['db_config'] def exec_sql(sql, values, is_query=False): try: flag = False #是否有異常 error = {} #若異常,保存錯誤信息 conn = pymysql.connect(host=dbconf['db_host'], port=dbconf['db_port'], user=dbconf['db_username'], passwd=dbconf['db_password'], db=dbconf['db_database'], charset=dbconf['db_charset']) with conn.cursor(pymysql.cursors.DictCursor) as cursor: num = cursor.execute(sql, values) #查詢結果集數量或執行影響行數 if is_query: #查詢取全部結果 result = cursor.fetchall() else: #執行提交 conn.commit() print('Sql: ', sql, ' Values: ', values) except Exception as err: flag = True error = err print('Error: ', err) finally: conn.close() if flag: return False, error, num if 'num' in dir() else 0 return True, result if 'result' in dir() else '', num
pymysql的查詢接口,能夠接受數組,元組和字典,本查詢接口使用數組形式來調用。如今此接口只支持與條件組合參數。json
def select(tablename, params={}, fields=[]): sql = "select %s from %s " % ('*' if len(fields) == 0 else ','.join(fields), tablename) ks = params.keys() where = "" ps = [] pvs = [] if len(ks) > 0: #存在查詢條件時,以與方式組合 for al in ks: ps.append(al + " =%s ") pvs.append(params[al]) where += ' where ' + ' and '.join(ps) rs = exec_sql(sql+where, pvs, True) print('Result: ', rs) if rs[0]: return {"code": 200, "rows": rs[1], "total": rs[2]} else: return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
以數組形式提供參數,錯誤信息解析與其它接口不一樣。flask
def insert(tablename, params={}): sql = "insert into %s " % tablename ks = params.keys() sql += "(`" + "`,`".join(ks) + "`)" #字段組合 vs = list(params.values()) #值組合,由元組轉換爲數組 sql += " values (%s)" % ','.join(['%s']*len(vs)) #配置相應的佔位符 rs = exec_sql(sql, vs) if rs[0]: return {"code": 200, "info": "create success.", "total": rs[2]} else: return {"code": 204, "error": rs[1].args[0], "total": rs[2]}
以字典形式提供參數,佔位符的形式爲:%(keyname)s,只支持按主鍵進行修改。後端
def update(tablename, params={}): sql = "update %s set " % tablename ks = params.keys() for al in ks: #字段與佔位符拼接 sql += "`" + al + "` = %(" + al + ")s," sql = sql[:-1] #去掉最後一個逗號 sql += " where _id = %(_id)s " #只支持按主鍵進行修改 rs = exec_sql(sql, params) #提供字典參數 if rs[0]: return {"code": 200, "info": "update success.", "total": rs[2]} else: return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
以字典形式提供參數,佔位符的形式爲:%(keyname)s,只支持按主鍵進行刪除。api
def delete(tablename, params={}): sql = "delete from %s " % tablename sql += " where _id = %(_id)s " rs = exec_sql(sql, params) if rs[0]: return {"code": 200, "info": "delete success.", "total": rs[2]} else: return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
提供默認的操做數據庫接口,實現基礎的業務邏輯,單表的CURD有它就足夠了。有複雜業務邏輯時,繼承它,進行擴展就能夠了。
import dbhelper class BaseDao(object): def __init__(self, table): self.table = table def retrieve(self, params={}, fields=[], session={}): return dbhelper.select(self.table, params) def create(self, params={}, fields=[], session={}): if '_id' in params and len(params) < 2 or '_id' not in params and len(params) < 1: #檢測參數是否合法 return {"code": 301, "err": "The params is error."} return dbhelper.insert(self.table, params) def update(self, params={}, fields=[], session={}): if '_id' not in params or len(params) < 2: #_id必須提供且至少有一修改項 return {"code": 301, "err": "The params is error."} return dbhelper.update(self.table, params) def delete(self, params={}, fields=[], session={}): if '_id' not in params: #_id必須提供 return {"code": 301, "err": "The params is error."} return dbhelper.delete(self.table, params)
根據客戶調用的rest方式不一樣,動態調用baseDao的相應方法,這個很關鍵,實現了它才能自動分配方法調用,才能只須要創建一個數據表,就自動提供CURD基本訪問功能。還好,動態語言能很方便的實現這種功能,感慨一下,node.js更方便且符合習慣^_^
method = { "GET": "retrieve", "POST": "create", "PUT": "update", "DELETE": "delete" } getattr(BaseDao(table), method[request.method])(params, [], {})
說明:
git clone https://github.com/zhoutk/pyrest.git cd pyrest export FLASK_APP=index.py flask run
至此,咱們已經實現了基本的框架功能,之後就是豐富它的羽翼。好比:session、文件上傳、跨域、路由改進(支持無縫切換操做數據庫的基類與子類)、參數驗證、基礎查詢功能加強(分頁、排序、模糊匹配等)。
感慨一下,好懷念在node.js中json對象的寫法,不用在key外加引號。
剛把基礎數據庫訪問類中的insert方法的參數形式改爲了字典,結果異常信息也正常了,文章再也不改動,有興趣者請自行查閱源代碼。