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 的服务通常是架构层面上的考虑. 因为它天生就具有很好的跨平台跨语言的集成能力 ...
随机推荐
- IdentityServer4使用OpenIdConnect实现单点登录
接上一篇:IdentityServer4实现OAuth2.0四种模式之授权码模式 前面写的四种OAuth2.0实现模式只涉及到IdentityServer4的OAuth2.0特性,并没有涉及到OenI ...
- OO——JML作业总结
目录 第三单元博客作业 JML语言理论基础 1.注释结构 2.JML表达式 3.方法规格 4.类型规格 应用工具链 JMLUnitNG使用实例 作业架构设计 第一次作业 第二次作业 第三次作业 BUG ...
- 详解html中的marquee属性
转自:https://www.jb51.net/web/531309.html 该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可 ...
- Android笔记(四十一) Android中的数据存储——SQLite(三)select
SQLite 通过query实现查询,它通过一系列参数来定义查询条件. 各参数说明: query()方法参数 对应sql部分 描述 table from table_name 表名称 colums s ...
- 用java刷剑指offer(二叉树中和为某一值的路径)
题目描述 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.(注意: 在返回值的list中,数组长度大 ...
- SOA=SOME/IP?你低估了这件事 | 第二弹
哈喽,大家好,第二弹的时间到~上文书说到v-SOA可以通过SOC.SORS和SOS来分解落地,第一弹中已经聊了SOC的实现,这部分也是国内各大OEM正在经历的阶段,第二弹,我们继续聊 ...
- django知识点回顾
1.web应用 本质是基于socket实现的应用程序 浏览器-----------服务器 2.http协议:应用层协议 1.基于TCP协议 2.基于请求响应 3.短连接 4.无状态保存(引入了cook ...
- Redis的缓存穿透问题和雪崩问题?
缓存穿透:就是访问redis中一个不存在的key的时候,会直接穿过缓存,去数据库中进行查询. 如果是黑客,进行恶意攻击的时候,每次都请求超过2000个/秒的时候,这个时候mysql基本上就挂了. 解决 ...
- [python]赶集网二手房爬虫插件【可用任意扩展】
最近应一个老铁的要求,人家是搞房产的,所以就写了这个二手房的爬虫,因为初版,所以比较简单,有能力的老铁可用进行扩展. import requests import os from bs4 impo ...
- C# 7.0 中的新特性((.NET Framework 4.7 与 Visual Studio 2017 ))
C#7.0 于 2017年3月 随 .NET 4.7 和 VS2017 发布. 一. out 变量(out variables) 以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了 ...