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

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. WebapiController的名字不能随便取名

    在做webapi时候,遇到一个很坑的问题,就是新增一个控制器时,当新增加的控制器名称叫:AccountController.cs 自己测试,通过postman 访问 都没有问题,但是前端调用会报错,说 ...

  2. js 使用 "use strict"

    "use strict"是JavaScript中一个非常好的特性,而且非常容易使用. 使用方法 // file.js "use strict" function ...

  3. ADO.NET 五(DataAdapter 与 DataSet)

    在执行对表中数据的查询时还能将数据保存到 DataSet 中,但需要借助 DataAdapter 类来实现. 在实际应用中,DataAdapter 与 DataSet 是在查询操作中使用最多的类. 此 ...

  4. 5_PHP数组_3_数组处理函数及其应用_8_数组与数据结构

    以下为学习孔祥盛主编的<PHP编程基础与实例教程>(第二版)所做的笔记. 数组与数据结构 1. array_push() 函数 程序: <?php $stack = array(&q ...

  5. 分享-SpringCloud微服务架构图

    1: 为大家分享一张SpringCloud微服务通用架构图 ​标题 此图仅供参考: 需要原图的同学请移步 >>>>>>>>> 这里 如有不合理的地 ...

  6. iOS - 动态库上架瘦身(去调虚拟机架构),不然验证会报错。

    eg: localhost:改造 M.emor.Y$ lipo WebRTC.framework/WebRTC -thin armv7 -output WebRTC_armv7localhost:改造 ...

  7. 轻量ORM-SqlRepoEx介绍

    轻量级 ORM-SqlRepoEx 介绍 SqlRepoEx是 .Net平台下兼容.NET Standard 2.0人一个轻型的ORM.解决了Lambda转Sql语句这一难题,SqlRepoEx使用的 ...

  8. 批量导入数据到InnoDB表速度优化

    1.使用Load data: 2. SET autocommit=0; ... SQL import statements ... COMMIT; 3. SET unique_checks=0; .. ...

  9. 阿里云SOP

    阿里云SOP 摘要 注册阿里云账号. 领取及配置ECS. 领取及配置RDS. 部署网站. 注册阿里云账号 在主页点击注册 填入相应的信息 领取及配置ECS 注册后领取免费的ECS,RDS. 打开控制台 ...

  10. MySQL查询(关联查询)

    一.mysql查询与权限 (一)数据库关联查询 **内连接查询(inner join)** 查询两个表共有的数据,交集 SELECT * FROM tb1 INNER JOIN tb2 ON 条件 所 ...