Flask一种通用视图,增删改查RESTful API的设计
模型设计是后端开发的第一步。数据模型反映了各种对象之间的相互关系。
from app import db
class Role(db.Model):
"""角色""" # TODO: 权限控制
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
users = db.relationship('User', backref='role_users', lazy='dynamic')
class User(db.Model):
"""用户"""
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
对于不同的数据模型之后,往往都要开发增删改查功能的页面或者API。而每个模型的接口写起来又有很多相似之处。因此便可以封装统一的操作API,来快速实现任意模型的增删改查功能。
这里采用RESTful API的典型设计,每个模型设计两个接口:列表接口和详情接口,支持的功能如下:
- 列表接口:GET获取列表,POST新建对象
- 详情接口:GET获取详情,PUT修改对象,DELETE删除对象。
比如Role对象可以设计以下两个接口:
@app.route('/roles', methods=['GET', 'POST'])
def roles():
...
@app.route('/roles/<int:role_id>', methods=['GET', 'PUT', 'DELETE'])
def role(role_id):
...
这里我们都使用复数roles来统一endpoint。
默认情况下,我们需要在每个方法下通过if reqeust.method进行判断然后进行相应增删改查的操作,如:
from flask import request, jsonify, abort
from models import db, Role
@app.route('/roles', methods=['GET', 'POST'])
def roles():
if request.method == 'GET':
role_list = Role.query.all()
# 序列化并返回响应
return jsonify([{'id': role.id, 'name': role.name, 'default': role.default} for obj in role_list]
else:
json_data = request.get_json():
name = json_data.get('name')
if name is None:
abort(404)
role = Role(name=name)
db.session.add(role)
db.commit()
return jsonify({'id': role.id, 'name': role.name, 'default': role.default})
@app.route('/roles/<int:role_id>', methods=['GET', 'PUT', 'DELETE'])
def role(role_id):
if request.method == 'GET':
role = Role.query.get_or_404(role_id)
return jsonify({'id': role.id, 'name': role.name, 'default': role.default})
elif request.method == 'PUT':
role = Role.query.get_or_404(role_id)
name = json_data.get('name')
if name is None:
abort(404)
role.name = name
db.commit()
return jsonify({'id': role.id, 'name': role.name, 'default': role.default})
else:
role = Role.query.get_or_404(role_id)
db.session.delete(role)
db.commit()
return jsonify({})
以上代码只简略处理了name一个参数。如果每个模型都要这样写一遍的话会非常繁琐。以下设计了一种通用的API视图。
# filename: utils.py
import datetime
from flask import jsonify, request
from models import db
LIST_METHODS = ['GET', 'POST'] # 资源列表默认方法,GET获取,POST新建
DETAIL_METHODS = ['GET', 'PUT', 'DELETE'] # 资源详情默认方法,GET获取,PUT修改,DELETE删除
def get_obj_fields(obj):
"""获取模型对象的表字段, obj或model均可"""
if obj is None:
return []
return [column.name for column in obj.__table__.columns]
def obj2dict(obj):
"""对象转字典"""
# 优先取模型fields字段指定的范围
fields = getattr(obj, 'fields') if hasattr(obj, 'fields') else get_obj_fields(obj)
obj_dict = {}
for field in fields:
value = getattr(obj, field)
if isinstance(value, (datetime.datetime, datetime.date)): # 处理datetime对象
value = value.isoformat()
obj_dict[field] = value
return obj_dict
def get_request_valid_data(obj):
data = request.get_json()
if data is not None:
data = {key: value for key, value in request.get_json().items()
if key in get_obj_fields(obj)}
return data
class Api(object):
"""通用Api"""
def __init__(self, model, obj_id=None):
self.model = model
self.obj_id = obj_id
self.data = get_request_valid_data(model)
if obj_id is not None:
self.obj = self.model.query.get_or_404(self.obj_id)
@property
def resource(self):
"""根据请求方法执行对应的操作,返回对象或列表"""
func = getattr(self, request.method.lower())
return func()
@property
def jsonify(self):
"""将资源结果转为JSON响应"""
res = self.resource
if isinstance(res, list):
return jsonify([obj2dict(obj) for obj in res])
else:
return jsonify(obj2dict(res))
class ListApi(Api):
"""资源列表通用Api"""
def get(self):
"""GET获取列表"""
obj_list = self.model.query.all()
return obj_list
def post(self):
"""POST创建对象"""
obj = self.model(**self.data)
db.session.add(obj)
db.session.commit()
return obj
class DetailApi(Api):
"""资源列表通用Api"""
def get(self):
"""GET获取详情"""
return self.obj
def put(self):
"""修改对象"""
[setattr(self.obj, key, value) for key, value in self.data.items()]
db.session.commit()
return self.obj
def delete(self):
"""删除对象"""
db.session.delete(self.obj)
db.session.commit()
使用方法为在views.py中
from models import Role, User
from utils import LIST_METHODS, DETAIL_METHODS, ListApi, DetailApi
@app.route('/roles', methods=LIST_METHODS)
def roles():
return ListApi(Role).jsonify
@app.route('/roles/<int:role_id>', methods=DETAIL_METHODS)
def role(role_id):
return DetailApi(Role, role_id).jsonify
这样每个模型的通用接口写起来就非常简单。
另外接口数据的验证方法可以参考如下参考如下,也可以在app层集中捕获并处理异常。
def verify_data(obj, data):
"""验证数据"""
if data is None:
return False
fields = get_obj_fields(obj)
columns = {column.name: column for column in obj.__table__.columns}
data = {key: value for key, value in data if key in fields} # 剔除非fields参数
for column in columns:
# 不检查主键和有默认值的列
if column.primary_key is True or column.default is not None:
continue
# nullable 验证
value = data.get(column.name)
column_name = column.name
if value is None:
if column.nullable is False:
return False, f'{column_name} 不能为空'
else:
column_type = str(column.type)
# Integer()类型验证
if column_type== 'INTEGER':
if not isinstance(value, int):
return False, f'{column_name} 需为int类型'
# todo 浮点型验证,datetime类型验证
# String()类型验证
if column_type.startswith('VARCHAR'):
length = int(column_type.split('(')[1][-1])
if not isinstance(value, str):
return False, f'{column_name} 需为str类型'
elif len(value) > length:
return False, f'{column_name} 长度不能大于 {length}'
return True, 'OK'
Flask一种通用视图,增删改查RESTful API的设计的更多相关文章
- HibernateTemplate、HibernateDaoSupport两种方法实现增删改查Good(转)
Spring+Hibernate两种方法实现增删改查 首先,定义一个Customer的bean类,设置好Customer.hbm.xml文件.再定义好一个Dao接口.准备好一个jdbc.propert ...
- EF(Entity Framework)通用DBHelper通用类,增删改查以及列表
其中 通用类名:DBhelper 实体类:UserInfo 1 //新增 2 DBHelper<UserInfo> dbhelper = new DBHelper<UserInfo& ...
- 封装自己通用的 增删改查的方法 By EF
封装自己的通用CURD By EF using System; using System.Collections.Generic; using System.Data.Entity; using Sy ...
- day25 crm 权限管理 通用的增删改查框架
代码: https://github.com/liyongsan/git_class/tree/master/day25/LuffyCRM
- HBase 增删改查Java API
1. 创建NameSpaceAndTable package com.HbaseTest.hdfs; import java.io.IOException; import org.apache.had ...
- ElasticSearch入门-增删改查(java api)
1.增加Index PutMappingRequest mapping = Requests.putMappingRequest(indices).type(mappingType).source(g ...
- EF学习笔记——通用增删改查方案
http://blog.csdn.net/leftfist/article/details/25005307 我刚接触EF未久,还不知道它有什么强大之处,但看上去,EF提供了一般的增删改查功能.以往用 ...
- Mybatis的学习总结(一)——使用配置文件实现增删改查
在使用Mybatis作为持久层来进行操作数据库,有很多的操作都是一样的,基本上都是先得到session,然后调用session提供的相关方法进行操作,接着提交session,最后关闭session.那 ...
- WCFRESTFul服务搭建及实现增删改查
WCFRESTFul服务搭建及实现增删改查 RESTful Wcf是一种基于Http协议的服务架构风格, RESTful 的服务通常是架构层面上的考虑. 因为它天生就具有很好的跨平台跨语言的集成能力 ...
随机推荐
- PCL提取圆柱系数
网上看了很多教程,没看到圆柱提取后的系数解释. 源码如下: #include <pcl/ModelCoefficients.h> #include <pcl/io/pcd_io.h& ...
- java容易混淆的概念
容易混淆的内容 1.JVM内存模型 2.Java内存模型 3.Java对象模型 JVM内存模型 1.堆 2.虚拟机栈 3.本地方法栈 4.程序计数器 5.方法区 Java内存模型 Java堆和方法区的 ...
- jmeter进行压测的步骤
1)安装jmeter和Badboy. 2)用badboy录制脚本,保存之后直接导出. 3)用jmeter打开badboy录制的脚本,假如是有参数的话,需要写一个csv的参数化文件,在jmeter中添加 ...
- Android笔记(四十四) Android中的数据存储——SQLite(六)整合
实现注册.登录.注销账户 MainActivity.java package cn.lixyz.activity; import android.app.Activity; import androi ...
- Android Exception Hook
承接上一篇文章Android Inline Hook,接下来我们看一下android系统中基于异常的hook方式,这种方式与inline hook相比实现较为简单,但执行效率是它的短板. except ...
- Spring Boot+STOMP解决消息乱序问题
当我们使用Spring Boot+websocket进行前后端进行通信时,我们需要注意:服务器可以随时向客户端发送消息.默认的情况下,不保证:服务器发送的消息与到达客户端的消息的顺序是一致的.可能先发 ...
- LeetCode - 86、分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前. 你应当保留两个分区中每个节点的初始相对位置. 示例: 输入: head = 1->4-&g ...
- mingw控制台中文乱码
乱码原因 直接先用一段话讲乱码原因,看不懂老老实实就往下看吧 其实我用Sublime或者VSCode等编辑器写代码出现的控制台中文乱码问题是编译器mingw输出的数据使用的是UTF-8编码,而控制台用 ...
- 配置logback日志管理的时候
在使用logback时候,需要引入 thymeleaf的配置 thymeleaf: suffix: .html check-template-location: true encoding: UTF- ...
- 容错(Fault-tolerance)
Spark Streaming的容错包括了三个地方的容错: 1.Executor失败容错:Executor的失败会重新启动一个新的Executor,这个是Spark自身的特性.如果Receiver所在 ...