模型设计是后端开发的第一步。数据模型反映了各种对象之间的相互关系。

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的设计的更多相关文章

  1. HibernateTemplate、HibernateDaoSupport两种方法实现增删改查Good(转)

    Spring+Hibernate两种方法实现增删改查 首先,定义一个Customer的bean类,设置好Customer.hbm.xml文件.再定义好一个Dao接口.准备好一个jdbc.propert ...

  2. EF(Entity Framework)通用DBHelper通用类,增删改查以及列表

    其中 通用类名:DBhelper 实体类:UserInfo 1 //新增 2 DBHelper<UserInfo> dbhelper = new DBHelper<UserInfo& ...

  3. 封装自己通用的 增删改查的方法 By EF

    封装自己的通用CURD By EF using System; using System.Collections.Generic; using System.Data.Entity; using Sy ...

  4. day25 crm 权限管理 通用的增删改查框架

    代码: https://github.com/liyongsan/git_class/tree/master/day25/LuffyCRM

  5. HBase 增删改查Java API

    1. 创建NameSpaceAndTable package com.HbaseTest.hdfs; import java.io.IOException; import org.apache.had ...

  6. ElasticSearch入门-增删改查(java api)

    1.增加Index PutMappingRequest mapping = Requests.putMappingRequest(indices).type(mappingType).source(g ...

  7. EF学习笔记——通用增删改查方案

    http://blog.csdn.net/leftfist/article/details/25005307 我刚接触EF未久,还不知道它有什么强大之处,但看上去,EF提供了一般的增删改查功能.以往用 ...

  8. Mybatis的学习总结(一)——使用配置文件实现增删改查

    在使用Mybatis作为持久层来进行操作数据库,有很多的操作都是一样的,基本上都是先得到session,然后调用session提供的相关方法进行操作,接着提交session,最后关闭session.那 ...

  9. WCFRESTFul服务搭建及实现增删改查

    WCFRESTFul服务搭建及实现增删改查 RESTful Wcf是一种基于Http协议的服务架构风格,  RESTful 的服务通常是架构层面上的考虑. 因为它天生就具有很好的跨平台跨语言的集成能力 ...

随机推荐

  1. showModalDialog的使用方法

    基本介绍: showModalDialog()         (IE 4+ 支持) showModelessDialog()      (IE 5+ 支持) window.showModalDial ...

  2. 并发编程之Callable异步,Future模式

    Callable 在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口.然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果.我们一般只能采用共享变 ...

  3. 【洛谷 P2408】 不同子串个数(后缀自动机)

    题目链接 裸体就是身体. 建出\(SAM\),\(DAG\)上跑\(DP\),\(f[u]=1+\sum_{(u,v)\in DAG}f[v]\) 答案为\(f[1]-1\)(因为根节点没有字符) # ...

  4. z7z8记录

    http://www.ypppt.com/ ppt模板地址

  5. 浅析JavaScript异步

    一直以来都知道JavaScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况.一般被问到异步的时候脑子里第一反应就是Ajax,setTimseout...这 ...

  6. js合并多个array

    Array.prototype.concat.call(array1, array2, array3, ...)

  7. require.context实现前端工程自动化

    require.context是什么 一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块,在前端工程中,如果遇到从一个文件夹引入很多 ...

  8. tensorflow 单机多GPU训练时间比单卡更慢/没有很大时间上提升

    使用tensorflow model库里的cifar10 多gpu训练时,最后测试发现时间并没有减少,反而更慢 参考以下两个链接 https://github.com/keras-team/keras ...

  9. 笔谈OpenGL ES(二)

    昨晚回家也看了OpenGL ES 2.0 iOS教程的第一篇,对于其中涉及的一些基本知识罗列下,虽然自己做iOS开发一年多了,但是对于一些细节没有注意,真正的把自己当成“应用”工程师了 ,不仅要会用, ...

  10. antd-table——内容展示变型

    bug单: https://github.com/ant-design/ant-design/issues/13825 1.设置固定宽度:在columns中设置widht或者className { t ...