SQLAlchemy+Flask-RESTful使用(一)
前言
开新坑啦.最近打算自己开一个资源聚合网站.就用Flask.
当然也使用了 Flask-RESTful和SQLAlchemy啦
写的过程中遇到过很多坑/觉得比较有意义的就写在这里.
变更记录
# 19.3.15 增加 SQLAlchemy查询数据里中文乱码的问题
# 19.2.18 增加 Flask-RESTful中序列化组件的使用方法
# 19.4.11 增加 序列化组件写在def中的书写方式
正文
前几章也讲过了基本的结合,这里主要讲讲常见问题 or 使用技巧
中文乱码(问号)
最先遇到的问题是乱码问题,数据库里的数据是这样的

经过flask传回后是这样的

解决方法是检查连接数据库时是否指定了数据库数据格式
指定数据库格式写在访问连接后类似GET请求传参
# 用户名:密码@访问地址:端口/数据库?编码
engine = create_engine('mysql+mysqldb://root:***@***:***/website?charset=utf8mb4')

序列化
说到ORM,就不得不说序列化.因为我们在使用ORM的时候,获取的obj是所有字段的集合.那么问题来了,如果 用户表 中包含了用户的密码字段,我们将所有字段全部返回明显是不合适的/正确做法是视开发逻辑来选择传递给前端哪些字段
序列化的方法有很多.适合自己的才是坠吼的.本人一直讨厌捧一踩一的人.
一种方法是依次取再放入字典中然后return字典(序列化),这样可以保证我们每次都重新指定了返回的字段.但是这样对后期的维护多有不便,比如如果POST和GET用户要求都返回一样的字段.是不是要在GET和POST里写上两段同样的代码?如果需求改变是不是要同时改变两段代码?
另一种方法是利用Flask-RESTful(以下省略为'官方')自带的序列化组件(EN官方文档)
https://flask-restful.readthedocs.io/en/latest/fields.html
如果我们使用 官方 带的序列化时,我们需要写一个序列化函数.序列化函数可以有多个参数,例如
resource_fields = {
# 设置序列化,k为序列化后的字段名/v为序列化的类型(如果重命名需要设置attribute/不写attribute代表与k同名的字段)
'id': fields.Integer, # integer代表数字
'name': fields.String, # String代表str
'money': fields.Integer(default=0), # 设置如果money无值默认为0
'status': fields.Integer,
'lv': fields.Integer(attribute='fk_vip_on_vip_lv'), # 设置该字段重命名为lv
'info': fields.String(attribute='VipInfo.info') # 该attr代表跨表到VipInfo的info字段(代表attr可以写表达式来跨表查询),注意这里的VipInfo是model中uselist所在行的字段名
}
编写完序列化我们怎样将obj与组件结合呢?文档给出两个方法
方法1:装饰器
class Vip(Resource):
# VIP信息(单)
resource_fields = {
# 设置序列化,k为序列化后的字段名/v为序列化的类型(如果重命名需要设置attribute/不写attribute代表与k同名的字段)
'id': fields.Integer, # integer代表数字
'name': fields.String, # String代表str
'money': fields.Integer(default=0), # 设置如果money无值默认为0
'status': fields.Integer,
'lv': fields.Integer(attribute='fk_vip_on_vip_lv'), # 设置该字段重命名为lv
'info': fields.String(attribute='VipInfo.info') # 该attr代表跨表到VipInfo的info字段(代表attr可以写表达式来跨表查询),注意这里的VipInfo是model中uselist所在行的字段名
} @marshal_with(resource_fields) # 将序列化组件结合/如obj为空也会返回, envelope指定返回的json里的序列化字典的名字(如不填则直接在最大的json中) # 写装饰器会导致return任意值都经过渲染/如规避使用一下方式 def get(self, vip_id): # 获取vip基本信息 from app.website.models import DBSession, Vip, VipInfo # 必须在函数内引入 session = DBSession() obj = session.query(Vip).join(VipInfo).filter(Vip.id==vip_id).first() return obj
使用装饰器会将所有请求在传回时强制序列化,这也带来了一个问题点,如果正常访问,一切看起来如此正常

但是当我们访问一个不存在的id时

这样的接口逻辑就可能让前后端对接出现问题.并且增加前端判断的工作量.大家都是打工的,何必苦苦为难呢?
如果我们先判断是否为空然后再返回呢?我们修改一下代码
class Vip(Resource):
# VIP信息(单)
resource_fields = {
# 设置序列化,k为序列化后的字段名/v为序列化的类型(如果重命名需要设置attribute/不写attribute代表与k同名的字段)
'id': fields.Integer, # integer代表数字
'name': fields.String, # String代表str
'money': fields.Integer(default=0), # 设置如果money无值默认为0
'status': fields.Integer,
'lv': fields.Integer(attribute='fk_vip_on_vip_lv'), # 设置该字段重命名为lv
'info': fields.String(attribute='VipInfo.info') # 该attr代表跨表到VipInfo的info字段(代表attr可以写表达式来跨表查询),注意这里的VipInfo是model中uselist所在行的字段名
} @marshal_with(resource_fields) # 将序列化组件结合/如obj为空也会返回, envelope指定返回的json里的序列化字典的名字(如不填则直接在最大的json中) # 写装饰器会导致return任意值都经过渲染/如规避使用一下方式 def get(self, vip_id): # 获取vip基本信息 from app.website.models import DBSession, Vip, VipInfo # 必须在函数内引入 session = DBSession() obj = session.query(Vip).join(VipInfo).filter(Vip.id==vip_id).first() if obj: return obj else: return None
结果:

为什么我直接写了无obj返回 None 还是不起作用呢?因为使用装饰器的方式会拦截所有返回强制序列化,所以找不到序列化字段所以都是默认值.
如果我们想更为灵活的,比如obj存在时再使用序列化,为空时传送我们定制的错误信息就需要使用方法2
方法2:手动使用序列化组件
Flask-RESTful官方也提供了自定义的方式,这种方式更为灵活.
class Vip(Resource):
# VIP信息(单)
vip_resource_fields = {
# 设置序列化,k为序列化后的字段名/v为序列化的类型(如果重命名需要设置attribute/不写attribute代表与k同名的字段)
'id': fields.Integer, # integer代表数字
'name': fields.String, # String代表str
'money': fields.Integer(default=0), # 设置如果money无值默认为0
'status': fields.Integer,
'lv': fields.Integer(attribute='fk_vip_on_vip_lv'), # 设置该字段重命名为lv
'info': fields.String(attribute='VipInfo.info') # 该attr代表跨表到VipInfo的info字段(代表attr可以写表达式来跨表查询),注意这里的VipInfo是model中uselist所在行的字段名
} # @marshal_with(resource_fields) # 将序列化组件结合/如obj为空也会返回, envelope指定返回的json里的序列化字典的名字(如不填则直接在最大的json中) # 写装饰器会导致return任意值都经过渲染/如规避使用一下方式 def get(self, vip_id, resource_fields=vip_resource_fields): # 必须映引入resource # 获取vip基本信息 from app.website.models import DBSession, Vip, VipInfo # 必须在函数内引入 session = DBSession() obj = session.query(Vip).join(VipInfo).filter(Vip.id==vip_id).first() if obj: dic = marshal(obj, resource_fields) # return时再指定序列化,不指示则不序列化 return info_tool.get_success_dic(dic) else: return info_tool.get_error_dic(1001, 'not user') return obj
# 我们也可以将图上的 vip_resource_fields 卸载def中(比如写在 get 中),这样我们就不用在get的参数中传 resource_fields ,其他不变即可
# info_tool模块是本人写的自用模块,功能有将 含有各种无法正常转json的字典转为正常的字典/定制封装返回值 等等.
# 此处的 get_success_dic 功能是在外层包一个dic,有一个code值作为前后端交流,信息在data里,例如

# get_error_success 同上

info_tool模块如下(19.3.18贴出,后续优化恕不通知)
# -*- coding=utf-8 -*-
# 将pymysql得到的dic转换为可以正常转json的dic
import time, datetime, decimal def kv_to_safe(k, v):
# 判断v的数据类型,转换成可json的类型
if type(v) == datetime.datetime:
# 如果是datetime类型则转换为时间戳
v = time.mktime(v.timetuple())
if type(v) == decimal.Decimal:
# 如果是decimal类型则转换为flaot
v = float(v)
if type(v) == bytes:
# bytes类型转str(utf8)
v = str(v, encoding='utf8')
return k, v def dict_to_safe(inf, code):
# dict转可转json的dict
if inf:
if type(inf) == dict:
info = {}
for k,v in inf.items():
k, v = kv_to_safe(k, v)
info[k] = v
elif type(inf) == list:
info = []
for i in inf:
dic = {}
for k,v in i.items():
k, v = kv_to_safe(k, v)
dic [k] = v
info.append(dic)
else:
info = None
dic = {
'code': code,
'data': info
}
return dic def oneobj_to_safe(model):
# 单一对象转dic
dic = {}
if model == None:
return None
for col in model._sa_class_manager.mapper.mapped_table.columns:
dic[col.name] = getattr(model, col.name)
return dic def allobj_to_safe(model_list):
# 多个对象转dic
dic_list = []
if model_list == None:
return None
for model in model_list:
dic_list.append(oneobj_to_safe(model))
return dic_list def get_error_dic(code, message):
# 生成错误json
dic = {
'code': code,
'data': {
'message': message
}
}
return dic def get_success_dic(dic):
# 生成正确json,code默认200
dic = {
'code': 200,
'data': dic
}
return dic
SQLAlchemy+Flask-RESTful使用(一)的更多相关文章
- Python Flask Restful
Flask Restful 1.flask restful 在flask基础上进行一些封装,主要用于实现restful接口 2.restful的理解 1)URI(统一资源标识符):每一个URI代表一 ...
- 使用swagger 生成 Flask RESTful API
使用swagger 生成 Flask RESTful API http://www.voidcn.com/article/p-rcvzjvpf-e.html swagger官网 https://swa ...
- Flask restful源码分析
Flask restful的代码量不大,功能比较简单 参见 http://note.youdao.com/noteshare?id=4ef343068763a56a10a2ada59a019484
- 如何用rflask快速初始化Flask Restful项目
如何用rflask快速初始化Flask Restful项目 说明 多啰嗦两句 我们在创建flask项目的时候,使用pycharm创建出来的项目比较简陋,而且随着项目的功能完善,项目目录结构会比较多,多 ...
- [flask]Restful接口测试简单的应用
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : shenqiang from flask import Flask,make_res ...
- 快速创建Flask Restful API项目
前言 Python必学的两大web框架之一Flask,俗称微框架.它只需要一个文件,几行代码就可以完成一个简单的http请求服务. 但是我们需要用flask来提供中型甚至大型web restful a ...
- Flask RESTful API搭建笔记
之前半年时间,来到项目的时候,已经有一些东西,大致就是IIS+MYSQL+PHP. 所以接着做,修修补补,Android/iOS与服务器数据库交换用PHP, Web那边则是JS+PHP,也没有前后端之 ...
- 七十八:flask.Restful之flask-Restful标准化返回参数以及准备数据
对于一个视图函数,可以指定好数据结构和字段用于返回,以后使用ORM模型或者自定义的模型的时候,它会自动获取模型中相应的字段,生成json数据,然后再返回给前端,这需要导入flask_restful.m ...
- python Flask restful框架
框架地址:https://github.com/flask-restful/flask-restful 文档:http://flask-restful.readthedocs.io/en/0.3.5/ ...
- python 全栈开发,Day142(flask标准目录结构, flask使用SQLAlchemy,flask离线脚本,flask多app应用,flask-script,flask-migrate,pipreqs)
昨日内容回顾 1. 简述flask上下文管理 - threading.local - 偏函数 - 栈 2. 原生SQL和ORM有什么优缺点? 开发效率: ORM > 原生SQL 执行效率: 原生 ...
随机推荐
- 重写URL
更新网页url /***省略部分代码***/ function rewriteUrl(url) { if (url.substr(0, 1) === "/") { url = (r ...
- Django(八)下:Model操作和Form操作、序列化操作
二.Form操作 一般会创建forms.py文件,单独存放form模块. Form 专门做数据验证,而且非常强大.有以下两个插件: fields :验证(肯定会用的) widgets:生成HTML(有 ...
- cocos 碰撞系统
前面的话 本文将简要介绍 Cocos Creator 中的碰撞系统,Cocos Creator 内置了一个简单易用的碰撞检测系统,支持圆形.矩形以及多边形相互间的碰撞检测 编辑碰撞组件 当添加了一个碰 ...
- 【简】题解 AWSL090429 【数塔问题】
因为每次只ban一个点 而且不是永久性的 预处理出每个点从上往下和从下往上的最大值 每次询问直接暴力 被ban掉点那行去掉那点的最大值 也可以直接预处理出每行的最大值和次大值 还有种做法貌似可以过 预 ...
- CF_528D
一句话题意 给你两个串s.t,长度为n.m,字符集为"ATGC",当且仅 当[i - k; i + k]中存在一个j,使得s[j ] = t[x]时,s[i ]可以 和t[x]匹配 ...
- Java动态代理实现及实际应用
一.代理的概念 动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是只知应用不懂实现. 动态代理技术就是用来产生一个对象的 ...
- Day053--MySQL
MySQL安装和基本管理https://www.cnblogs.com/majj/p/9160383.html 管理员模式运行cmd 打开终端,输入mysqld,打开服务端. 打开终端,输入mysql ...
- Linux 内核文档翻译 - kobject.txt
原文地址:Linux 内核文档翻译 - kobject.txt 作者:qh997 Everything you never wanted to know about kobjects, ksets, ...
- java中getAttribute与getParameter方法的区别
知识点1:getAttribute表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为object对象类型 例: r ...
- 实验二 Java面向对象程序设计实验报告
实验二 Java面向对象程序设计 实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O.L.I.D原则 5.了解设计模式 实验 ...