本文正在参加「Python主题月」,详情查看活动链接
导语
简单总结相关Flask模型层用到的插件:Flask-SQLAlchemy和Flask-Migrate,分别用于操作数据库以及跟踪数据库迁移;归纳开发过程中会使用到的几个场景和常用的方法。关于Flask-SQLAlchemy一些复杂的增删改查操作涉及到比较多的场景,本文暂不涉及。
关于ORM
通过ORM框架,可以方便快捷的进行数据库操作,同时提高规范性,降低操作性,提高开发效率。同时具备支持多种类型数据库,即提供了高层ORM操作,同时也可以使用底层的SQL原生功能。
常见Python ORM框架:
- Django's ORM(易用易上手,缺点性能问题)
- SqlAlchemy(灵活设计,缺点学习成本高)
Flask-Sqlalchemy扩展
基于SqlAlchemy,Flask-Sqlalchemy扩展简化了Flask项目中使用的SqlAlchemy的操作。
Flask-Sqlalchemy简单应用
from flask import Flask
from flask-sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
__table__name = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
备注:
- app的应用配置项SQLALCHEMY_DATABASE_URI指定了SQLAlchemy所要操作的数据库;
- db对象是SQLAlchemy类的实例,表示程序使用的数据库;
- 定义的User模型对应着数据库中的表,__tablename__定义了在数据库中使用的表明,如果该变量没有定义,Flask-SQLAlchemy会使用一个默认的名字;
Flask-Sqlalchemy表初始化:
db.create_all()
Flask-Sqlalchemy表操作:
admin = User('admin', 'admin@example.com')
guest = User('guest', 'guest@example.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
user = User.query.filter_by(username="admin").first()
print(user.id, user.username, user.email)
备注:
- 将需要提交的数据提交到会话后,最后通过db.session.commit()提交事务,最终才会将数据写入到数据库中;
进阶使用
Flask-SQLAlchemy扩展能够识别的配置项:
- SQLALCHEMY_DATABASE_URI:用于连接数据库;
- SQLALCHEMY_BINDS:用于SQLAlchemy绑定多个数据库;
- SQLALCHEMY_ECHO:设置为True,SQLAlchemy将会记录所有发到标准输出(stderr)的语句,主要用于调试,默认为False;
- SQLALCHEMY_POOL_SIZE:数据库连接池大小;
- SQLALCHEMY_POOL_TIMEOUT:数据库连接池超时时间;
- SQLALCHEMY_POOL_RECYCLE:自动回收连接的秒数,对于Mysql,Mysql会自动回收闲置超过八小时的连接,Flask-SQLAlchemy默认回收超过两小时的连接;
- SQLALCHEMY_TRACK_MODIFICATIONS:Flask-SQLAlchemy将会追踪对象的修改并且发送信号,将会消耗内存,默认为True,不需要可关闭;
绑定多个数据库
# -*- coding: utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_BINDS"] = {"user":"sqlite:///user.db", "film": "sqlite:///film.db"}
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True
db = SQLAlchemy(app)
class User(db.Model):
__bind_key__ = "user"
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_name = db.Column(db.String(20), unique=True)
user_email = db.Column(db.String(40), unique=True)
def __init__(self):
self.session = db.session
def set_user(self, user_name, user_email):
self.user_name = user_name
self.user_email = user_email
self.session.add(self)
self.session.commit()
if __name__ == "__main__":
db.create_all(bind="user")
user = User()
user.set_user("wangyangming", "wangyangming@email.com")
备注:
- SQLALCHEMY_BINDS配置项在字典中可以声明多个数据库连接;
- User模型中__bind_key__属性用于指定绑定连接;
- db.create_all()可以作用所有声明的绑定,也可作用指定绑定,drop_all方法也是一样;
声明模型(一对多关系)
# -*- coding: utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
user = 'root'
password = 'root'
database = 'test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://%s:%s@127.0.0.1:3306/%s?charset=gbk' % (user, password, database)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_name = db.Column(db.String(20), unique=True)
user_addr = db.relationship("Address", backref="user", lazy='dynamic')
def __init__(self):
self.session = db.session
def set_user(self, user_name, user_email):
self.user_name = user_name
addr = Address(email=user_email, user=self)
self.session.add(self)
self.session.add(addr)
self.session.commit()
class Address(db.Model):
__tablename__ = "address"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(40), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
if __name__ == "__main__":
user = User()
user.set_user("wangyangming", "wangyangming@email.com")
address = Address.query.filter_by(email="wangyangming@email.com").first()
if address:
print(address.id)
print(address.user.user_name)
备注:
- db.relationship函数定义关联的表,backref为Address提供一个新的属性,可以使用Address.user获取使用该地址的人,lazy可以决定SQLAlchemy从什么时候获取数据,'dynamic' 在有多条数据的时候是特别有用的,不是直接加载这些数据;
- 外键通过ForeignKey单独声明;
Flask-Migrate扩展
处理SQLAlchemy数据库迁移的扩展,通过Flask命令行接口对数据库进行操作。对数据库升级和降级。
Flask-Migrate使用:
from flask_migrate import Migrate
migrate = Migrate(app, db)
Flask-Migrate常用命令:
flask db init
flask db migrate
flask db upgrade
命令解析:
- init:新建一个名字为migrations的文件夹,并且记录一个数据库版本号,一份保留在migrations中,一份保存在数据库中(新建一张名字为alembic_version的表来保存)
- migrate:在migrations文件夹中生成迁移文件。(迁移脚本不会检测models 的所有变更, Alembic 目前无法检测表名修改,列名修改)
- upgrade:将修改应用到数据库中。