资讯专栏INFORMATION COLUMN

用python写通用restful api service(二)

liangzai_cool / 2272人阅读

摘要:今天项目已经能够做一个简单的后端服务了,在中新建一个表,就能自动提供的服务了。函数用来区分是查询还是执行操作。字段组合值组合,由元组转换为数组配置相应的占位符修改接口以字典形式提供参数,占位符的形式为,只支持按主键进行修改。

</>复制代码

  1. 今天项目已经能够做一个简单的后端服务了,在mysql中新建一个表,就能自动提供restful api的CURD服务了。

关键点

根据REST的四种动词形式,动态调用相应的CURD方法;

编写REST与基础数据库访问类之间的中间层(baseDao),实现从REST到数据访问接口之间能用业务逻辑处理;

编写基础数据库访问类(dehelper),实现从字典形式的参数向SQL语句的转换;

实现的rest-api

实现了如下形式的rest-api

</>复制代码

  1. [GET]/rs/users/{id}
  2. [GET]/rs/users/key1/value1/key2/value2/.../keyn/valuen
  3. [POST]/rs/users
  4. [PUT]/rs/users/{id}
  5. [DELETE]/rs/users/{id}
基础数据库访问类

该类实现与pymysql库的对接,提供标准CURD接口。

准备数据库表

在数据库对应建立users表,脚本如下:

</>复制代码

  1. CREATE TABLE `users` (
  2. `_id` int(11) NOT NULL AUTO_INCREMENT,
  3. `name` varchar(32) CHARACTER SET utf8mb4 DEFAULT "" COMMENT "标题名称",
  4. `phone` varchar(1024) DEFAULT "",
  5. `address` varchar(1024) DEFAULT NULL,
  6. `status` tinyint(4) DEFAULT "1" COMMENT "状态:0-禁;1-有效;9删除",
  7. `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT "创建时间",
  8. PRIMARY KEY (`_id`),
  9. UNIQUE KEY `uuid` (`_id`) USING BTREE
  10. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT="表";
新建数据库配置文件(configs.json)

数据连接配置,不入版本库。

</>复制代码

  1. {
  2. "db_config": {
  3. "db_host": "ip",
  4. "db_port": 1234,
  5. "db_username": "root",
  6. "db_password": "******",
  7. "db_database": "name",
  8. "db_charset": "utf8mb4"
  9. }
  10. }
对接pymysql接口

用函数exec_sql封装pymysql,提供统一访问mysql的接口。is_query函数用来区分是查询(R)还是执行(CUD)操作。出错处理折腾了好久,插入异常返回的错误形式与其它的竟然不一样!返回参数是一个三元组(执行是否成功,查询结果或错误对象,查询结果数或受影响的行数)

</>复制代码

  1. with open("./configs.json", "r", encoding="utf-8") as json_file:
  2. dbconf = json.load(json_file)["db_config"]
  3. def exec_sql(sql, values, is_query=False):
  4. try:
  5. flag = False #是否有异常
  6. error = {} #若异常,保存错误信息
  7. conn = pymysql.connect(host=dbconf["db_host"], port=dbconf["db_port"], user=dbconf["db_username"],
  8. passwd=dbconf["db_password"], db=dbconf["db_database"], charset=dbconf["db_charset"])
  9. with conn.cursor(pymysql.cursors.DictCursor) as cursor:
  10. num = cursor.execute(sql, values) #查询结果集数量或执行影响行数
  11. if is_query: #查询取所有结果
  12. result = cursor.fetchall()
  13. else: #执行提交
  14. conn.commit()
  15. print("Sql: ", sql, " Values: ", values)
  16. except Exception as err:
  17. flag = True
  18. error = err
  19. print("Error: ", err)
  20. finally:
  21. conn.close()
  22. if flag:
  23. return False, error, num if "num" in dir() else 0
  24. return True, result if "result" in dir() else "", num
查询接口

pymysql的查询接口,可以接受数组,元组和字典,本查询接口使用数组形式来调用。现在此接口只支持与条件组合参数。

</>复制代码

  1. def select(tablename, params={}, fields=[]):
  2. sql = "select %s from %s " % ("*" if len(fields) == 0 else ",".join(fields), tablename)
  3. ks = params.keys()
  4. where = ""
  5. ps = []
  6. pvs = []
  7. if len(ks) > 0: #存在查询条件时,以与方式组合
  8. for al in ks:
  9. ps.append(al + " =%s ")
  10. pvs.append(params[al])
  11. where += " where " + " and ".join(ps)
  12. rs = exec_sql(sql+where, pvs, True)
  13. print("Result: ", rs)
  14. if rs[0]:
  15. return {"code": 200, "rows": rs[1], "total": rs[2]}
  16. else:
  17. return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
插入接口

以数组形式提供参数,错误信息解析与其它接口不同。

</>复制代码

  1. def insert(tablename, params={}):
  2. sql = "insert into %s " % tablename
  3. ks = params.keys()
  4. sql += "(`" + "`,`".join(ks) + "`)" #字段组合
  5. vs = list(params.values()) #值组合,由元组转换为数组
  6. sql += " values (%s)" % ",".join(["%s"]*len(vs)) #配置相应的占位符
  7. rs = exec_sql(sql, vs)
  8. if rs[0]:
  9. return {"code": 200, "info": "create success.", "total": rs[2]}
  10. else:
  11. return {"code": 204, "error": rs[1].args[0], "total": rs[2]}
修改接口

以字典形式提供参数,占位符的形式为:%(keyname)s,只支持按主键进行修改。

</>复制代码

  1. def update(tablename, params={}):
  2. sql = "update %s set " % tablename
  3. ks = params.keys()
  4. for al in ks: #字段与占位符拼接
  5. sql += "`" + al + "` = %(" + al + ")s,"
  6. sql = sql[:-1] #去掉最后一个逗号
  7. sql += " where _id = %(_id)s " #只支持按主键进行修改
  8. rs = exec_sql(sql, params) #提供字典参数
  9. if rs[0]:
  10. return {"code": 200, "info": "update success.", "total": rs[2]}
  11. else:
  12. return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
删除接口

以字典形式提供参数,占位符的形式为:%(keyname)s,只支持按主键进行删除。

</>复制代码

  1. def delete(tablename, params={}):
  2. sql = "delete from %s " % tablename
  3. sql += " where _id = %(_id)s "
  4. rs = exec_sql(sql, params)
  5. if rs[0]:
  6. return {"code": 200, "info": "delete success.", "total": rs[2]}
  7. else:
  8. return {"code": rs[1].args[0], "error": rs[1].args[1], "total": rs[2]}
中间层(baseDao)

提供默认的操作数据库接口,实现基础的业务逻辑,单表的CURD有它就足够了。有复杂业务逻辑时,继承它,进行扩展就可以了。

</>复制代码

  1. import dbhelper
  2. class BaseDao(object):
  3. def __init__(self, table):
  4. self.table = table
  5. def retrieve(self, params={}, fields=[], session={}):
  6. return dbhelper.select(self.table, params)
  7. def create(self, params={}, fields=[], session={}):
  8. if "_id" in params and len(params) < 2 or "_id" not in params and len(params) < 1: #检测参数是否合法
  9. return {"code": 301, "err": "The params is error."}
  10. return dbhelper.insert(self.table, params)
  11. def update(self, params={}, fields=[], session={}):
  12. if "_id" not in params or len(params) < 2: #_id必须提供且至少有一修改项
  13. return {"code": 301, "err": "The params is error."}
  14. return dbhelper.update(self.table, params)
  15. def delete(self, params={}, fields=[], session={}):
  16. if "_id" not in params: #_id必须提供
  17. return {"code": 301, "err": "The params is error."}
  18. return dbhelper.delete(self.table, params)
动态调用CURD

根据客户调用的rest方式不同,动态调用baseDao的相应方法,这个很关键,实现了它才能自动分配方法调用,才能只需要建立一个数据表,就自动提供CURD基本访问功能。还好,动态语言能很方便的实现这种功能,感慨一下,node.js更方便且符合习惯^_^

</>复制代码

  1. method = {
  2. "GET": "retrieve",
  3. "POST": "create",
  4. "PUT": "update",
  5. "DELETE": "delete"
  6. }
  7. getattr(BaseDao(table), method[request.method])(params, [], {})

说明:

table是前一章中解析出来的数据表名,这块就是users;

method应该是定义一个常量对象,对应rest的动词,因为对ypthon不熟,定义了一个变量先用着,查了下常量说明,看着好复杂;

request.method 客户请求的实际rest动词;

params是前一章中解析出来的参数对象;

完整代码

</>复制代码

  1. git clone https://github.com/zhoutk/pyrest.git
  2. cd pyrest
  3. export FLASK_APP=index.py
  4. flask run
小结

至此,我们已经实现了基本的框架功能,以后就是丰富它的羽翼。比如:session、文件上传、跨域、路由改进(支持无缝切换操作数据库的基类与子类)、参数验证、基础查询功能增强(分页、排序、模糊匹配等)。
感慨一下,好怀念在node.js中json对象的写法,不用在key外加引号。

补丁

刚把基础数据库访问类中的insert方法的参数形式改成了字典,结果异常信息也正常了,文章不再改动,有兴趣者请自行查阅源代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41052.html

相关文章

  • pythonrestful api service(一)

    摘要:项目介绍用语言来写一个,数据库使用。如此选择,主要目的是针对中小规模的网络应用,能充分利用关系数据库的种种优势,来实现丰富的现代互联网应用。经搜索,还需要配置后面两个环境变量才能正常运行,大概原因是版本与之间的区别。 一直在用node.js做后端,要逐步涉猎大数据范围,注定绕不过python,因此决定把一些成熟的东西用python来重写,一是开拓思路、通过比较来深入学习python;二...

    LoftySoul 评论0 收藏0
  • 自动化代码生成工具 Snips 开发实践

    摘要:今天我来和大家分享一下以及自动化生成工具的开发经验。代码生成工具接着讲讲代码生成工具,对于来讲,有官方的代码生成器,还有其他的同类开源项目比如。现有的代码生成器没有可以开箱即用的,都需要去进行不少的修改。 前言 在开发工作中,经常会遇到新产品、服务上线后,需要将其 API 编写不同语言的 SDK。但不同语言 SDK 中都有很大一部分内容是用来进行 API 的描述,而且这部分代码量是最大...

    only_do 评论0 收藏0
  • 自动化代码生成工具 Snips 开发实践

    摘要:今天我来和大家分享一下以及自动化生成工具的开发经验。代码生成工具接着讲讲代码生成工具,对于来讲,有官方的代码生成器,还有其他的同类开源项目比如。现有的代码生成器没有可以开箱即用的,都需要去进行不少的修改。 前言 在开发工作中,经常会遇到新产品、服务上线后,需要将其 API 编写不同语言的 SDK。但不同语言 SDK 中都有很大一部分内容是用来进行 API 的描述,而且这部分代码量是最大...

    qc1iu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<