从上期开始我们完成了项目的最简单的演示,配置两个不同路径,就完成Http四个动词的操作,Get(所有,以及通过ID获取)、Post(新增)、Delete(通过ID删除)、Put(Patch的操作,Put会将未填写的字段置空)。
由于resource的url是两个不同的路径注册,所以四个动词五大操作是分别放置在两个类中的,如Todo以及TodoList中。其中TodoList包含Get(GetAll,可以加分页),Post(新增),Todo则在URL中有Id信息,可以具体到单个实体,所以有Get(获取单个实体),Delete(删除)、Put(Patch更新)。
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/')
下面开始完整的从实体类到restful四个动词五大操作的过程。
1、定义实体类
根据具体需求和业务,定义实体类,如Developer(开发人员)。需要保存开发人员的
编号(code),姓名(name),描述(desc),类型(type如Android,后台等)。
编写Developer类,按1、准备工作中说明要实现三个基类:db.Model, JsonModel, DbOperate,其中增加ID主键,自增。
class Developer(db.Model, JsonModel, DbOperate):
__tablename__ = 'developers'
tojson = "id,code,name,desc,type"
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(256), nullable=False)
name = db.Column(db.String(256), nullable=False)
desc = db.Column(db.TEXT, nullable=True)
type = db.Column(db.String(256), nullable=True)
date_created = db.Column(db.DateTime, default=db.func.now())
date_modified = db.Column(
db.DateTime, default=db.func.now(),
onupdate=db.func.now())
其中date_created:为创建进时间,
date_modified:为修改时间,密码为操作当前时间。
将Developer放在App\models.py中,现在开始编写入口路径路由,以及定义Resource。
在app\api\__init__.py中导入DevelopersResouce以及DeveloperResouce,从而配置好路由。
api.add_resource(DevelopersResouce, "/api/v1.0/developers")
api.add_resource(DeveloperResouce, "/api/v1.0/developers/")
现在主要面对的是DevelopersResouce和DeveloperResouce,代码如下:
from ..bizviews import IDResource,IDsResource
from ..models import Developer
class DevelopersResouce(IDsResource):
def getModel(self):
return Developer
class DeveloperResouce(IDResource):
def getModel(self):
return Developer
现在的问题是基类IDResource和IDsResource到底做了些什么?
为什么能达到不用编写代码就可以实现四个Http动词五个动作,先看代码:
from flask_restful import Resource
from .restfuls import doGet, dopostordeleteorput, doerror
from flask import request
class IDResource(Resource):
def __init__(self):
self.cls = self.getModel()
def getModel(self):
pass
def get(self, id):
def handler(user_id):
module = self.cls.query.get(id)
return module
return doGet(handler)
def put(self, id):
def handler(user_id):
module = self.cls.fromDict(request.json)
oldmodule = self.cls.query.get(id)
if oldmodule is None:
oldmodule = self.cls(id=id)
oldmodule.putUpdate(module)
oldmodule.save()
return dopostordeleteorput(handler)
def delete(self, id):
def handler(user_id):
oldmodule = self.cls.query.get(id)
oldmodule.delete()
return dopostordeleteorput(handler)
class IDsResource(Resource):
def __init__(self):
self.cls = self.getModel()
def getModel(self):
pass
def get(self):
def handler(user_id):
users = self.cls.query.all()
return users
return doGet(handler)
def post(self):
def handler(user_id):
module = self.cls.fromDict(request.json)
if self.checkpost(user_id, module):
return doerror(667)
self.prepost(module)
module.save()
self.afterPost(module)
return dopostordeleteorput(handler)
def checkpost(self, user_id, module):
return self.cls.query.filter_by(name=module.name) \
.first() is not None
def prepost(self, module):
pass
def afterPost(self, module):
pass
相同代码出现时就应该考虑提取函数,拥抱变化,将变化的和不变的分开,通过多态或泛型等能想到的办法去保证只有一份代码。
先看IDsResource,实现了get和post方法,其中get方法相对简单,只需要cls类。由于Python无法像Java、C#这样写泛型,所以人为定义一个方法 def getModel(self),实现IDsResouce的子类override该方法,从而达到子类使用具体的类。也将变化的部分和不变的部分分开。尤其是Post方法,还做了很多预留,checkpost是在保存前进行检测,比如code不能重复,就需要子类重写该方法,而prepost是保存前操作,afterPost则是保存后操作。
IDResouce相对来说比较简单,只有Put方法有一些复杂的操作,其实在delete时也应该有预留方法,比如关联的表数据也要删除,这里只要是Demo,所以没有处理。比如IDsResouce的get方法也没有做分页处理。
测试:第一次访问
此时没有任何数据,测试Post新增,编写Json:
{
"code":"hwh",
"name":"黄卫华",
"type":"android"
}
测试:
测试查看结果:
测试单个用例:
这里一直有一个token在这里,就涉及到登录信息,以及Token怎样验证的情况。
仔细查看IDsResouce和IDResouce就会发现里有几个不太认识的方法:
doGet:用于处理Get方法所做操作模板
dopostordeleteorput:用于处理其他三个方法所做操作模板
具体实现代码:
def doGet(handler):
token = request.headers.get('token')
if token:
user_id, status = User.decode_token(token)
if status == 200:
return dosuccess(handler(user_id))
else:
return doerror(status)
return doerror(666)
def dosuccess(users):
response = Response()
response.setData(users)
return response.toDict() , 200
def doerror(status):
response = Response(status=status)
return response.toDict() , status
def doexception(msg):
response = Response()
response.setException(msg)
return response.toDict(), 999
def dopostordeleteorput(handler):
token = request.headers.get('token')
if token:
user_id, status = User.decode_token(token)
if status == 200:
try:
handler(user_id)
return dosuccess(None)
except DataExistsException:
return doerror(667)
except Exception as e:
return doexception(str(e))
else:
return doerror(status)
else:
doerror(666)
token = request.headers.get('token') 获取头件中的token,然后进行验证,
user_id, status = User.decode_token(token)
详见代码。这种办法代码也只在一个地方编写,还有更优雅的办法通过装饰器,以后会进一步介绍。
其他的几个实体类的资源文件也是类似的如下:
from ..bizviews import IDResource, IDsResource
from ..models import User
class UsersResouce(IDsResource):
def getModel(self):
return User
class UserResouce(IDResource):
def getModel(self):
return User
from ..bizviews import IDResource,IDsResource
from ..models import ModuleInfo
class ModuleInfosResouce(IDsResource):
def getModel(self):
return ModuleInfo
class ModuleInfoResouce(IDResource):
def getModel(self):
return ModuleInfo
到现在为至,基本功能完成。而标准的Restful的编写也非常简单,只要配置一下GetModel就可以,以后会进一步讲解授权验证以及权限。请继续关注。
请关注我的公众号:9i编程9i编程