书接上文flask与DB交互,我们了解到
- 如何建立与DB——SQLite的连接
- 如何变更逻辑,建立表与数据的关系,让资料躺入DB
- 如何优化项目结构
- 如何用with优化DB的连接管理
这一章我们将用ORM的力量优化数据库操作。
什么是ORM
ORM是用来把对象模型表示的对象映射到基于SQL的关系模型数据库结构中去。简明点说,数据库中每一行就是一个对象。
示例代码
开始SQLAlchemy
安装
> pip3 install Flask-SQLAlchemy
更加优秀的项目工程学:
- 创建文件夹models,归类为数据结构定义以及数据操作
- 创建文件夹resources,归类为HTTP动作(Verbs)响应操作
- 创建文件夹templates,归类为HTML页面
每一个页面我们都可以看成这三者的结合(当然单纯的API是不需要HTML页面的)
然后我们就该迁移之前的项目,以符合上述的规范

接下来就迁移项目提出一些例子:
比如我们的原文件item.py
from flask_restful import Resource, reqparse
from flask_jwt import jwt_required
import sqlite3
class Item(Resource):
TABLE_NAME = 'items'
parser = reqparse.RequestParser()
parser.add_argument('price',
type=float,
required=True,
help="This field cannot be left blank!"
)
@jwt_required()
def get(self, name):
item = self.find_by_name(name)
if item:
return item
return {'message': 'Item not found'}, 404
@classmethod
def find_by_name(cls, name):
connection = sqlite3.connect('data.db')
cursor = connection.cursor()
query = "SELECT * FROM {table} WHERE name=?".format(table=cls.TABLE_NAME)
result = cursor.execute(query, (name,))
row = result.fetchone()
connection.close()
#不完整,只截取一部分
其实很明显的可以看出来get是我们常规定义的HTTP动作(Verbs),但是find_by_name是数据库操作。如果要把item.py拆解成model和resource两个部分,归根究底就是要把这两种函数拆分下来
#model/item.py
import sqlite3
class ItemModel(db.Model):
@classmethod
def find_by_name(cls, name):
connection = sqlite3.connect('data.db')
cursor = connection.cursor()
query = "SELECT * FROM {table} WHERE name=?".format(table=cls.TABLE_NAME)
result = cursor.execute(query, (name,))
row = result.fetchone()
connection.close()
#resource/item.py
from flask_restful import Resource, reqparse
from flask_jwt import jwt_required
from models.item import ItemModel
class Item(Resource):
TABLE_NAME = 'items'
parser = reqparse.RequestParser()
parser.add_argument('price',
type=float,
required=True,
help="This field cannot be left blank!"
)
@jwt_required()
def get(self, name):
item = ItemModel.find_by_name(name)
if item:
return item
return {'message': 'Item not found'}, 404
以上其实是一个非常简单的操作,接下来我们需要慢慢的重构整个项目,如果前期就没有区分DB操作和HTTP动作的话,这是一个挺大的工程。当重构结束时,我们引入SQLAlchemy的概念
用SQLAlchemy重构models的内容
SQLAlchemy初体验.\db.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
修改app.py以导入db设定
#DB的连接词
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
#默认True,SQLAlchemy会记录下对象的变动,可以理解成写log
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
#此处是注册DB。这一点蛮重要的,鱼刚摸索是也是不知道怎么把DB的定义拆解出来,原来是要在此处注册DB,这样就可以在app.py文件外处理DB相关内容。
if __name__ == '__main__':
from db import db
db.init_app(app)
修改item.py以导入db设定
from db import db
将表映射为对象
class ItemModel(db.Model):
__tablename__ = 'items'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
price = db.Column(db.Float(precision=2))#两位小数
这样我们就把表item映射成了class:ItemModel,接下来就是以同样的方法改造user.py
改造原DB交互方法
用SQLAlchemy寻找数据
@classmethod
def find_by_name(cls, name):
return cls.query.filter_by(name=name).first()
#这句语句等效于```SELECT * FROM items WHERE name = name LIMIT 1```
用SQLAlchemy insert&update数据
def save_to_db(self):
db.session.add(self)
db.session.commit()
SQLAlchemy的session.add有点类似于ORACLE的MERGE,即存在更新,不存在新增
用SQLAlchemy delete数据
def delete_from_db(self):
db.session.delete(self)
db.session.commit()
现在再看下自己的models\item.py,会发现简单了非常多
接着优化models\item.py对应的resources\item.py
from models.item import ItemModel
class Item(Resource):
#此处省略reqparse的定义
def post(self, name):
if ItemModel.find_by_name(name):
return {'message': "An item with name '{}' already exists.".format(name)}, 400
data = Item.parser.parse_args()
item = ItemModel(name, **data)
try:
item.save_to_db()
except:
return {"message": "An error occurred inserting the item."}, 500
return item.json(), 201
class ItemList(Resource):
def get(self):
return {'items': list(map(lambda x: x.json(), ItemModel.query.all()))}
#这句话等效于```select * from items```
下面就看大家去优化user.py了
创建表
到这边,item.py和user.py的优化基本完成了,却缺了个任务:创建表。
app.py中
api = Api(app)
#请求钩子,又称装饰器,还有一个名字,请求的中间件。作用第一次请求之前执行
@app.before_first_request
def create_tables():
#运行models,以创建不存在的表
db.create_all()
接下来为了体现外键的作用,教学又新增了新表stores,下面粗略的说明一下
更多有关SQLAlchemy的问题
在SQLAlchemy中的映射关系
可以详细阅读这篇文章
relationship中的 lazy屬性
如下举例,教学视屏是一个一对多的关系,store是父表,item是子表,item的store_id作为外键关联store的id
store.py
from db import db
class StoreModel(db.Model):
__tablename__ = 'stores'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
items = db.relationship('ItemModel', lazy='dynamic')
item.py
from db import db
class ItemModel(db.Model):
__tablename__ = 'items'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
price = db.Column(db.Float(precision=2))
store_id = db.Column(db.Integer, db.ForeignKey('stores.id'))
store = db.relationship('StoreModel')
其实store_id = db.Column(db.Integer, db.ForeignKey('stores.id'))
这一句话已经定义了外键关系,至于relationship的定义其实是为了在子表?或者父表?中可以取到父表?或者子表的值。具体的理解就要看上面引用的两篇文章了。
到这里SQLAlchemy给予我们的力量已经显而易见了。希望大家在没讲到的部分自行磨练。比如继续拓展store.py
总结,我们学到了:
如何规范flask工程的项目结构
什么是ORM
如何使用SQLAlchemy
一些更多的有关SQLAlchemy的知识
下一章
flask配置生产环境
网友评论