1.数据库
1. Flask-SQLAlchemy管理数据库
Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用 SQLAlchemy 的操作。
在这些 URL 中,hostname 表示 MySQL 服务所在的主机,可以是本地主机(localhost),也可以是远程服务器。数据库服务器上可以托管多个数据库,因此 database 表示要使用的数据库名。如果数据库需要进行认证,username 和 password 表示数据库用户密令。
# 配置数据库 sqlite
from flask_sqlalchemy import SQLAlchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
# 使用的 数据库 URL 根据数据库不同 按照上图进行配置
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
# 设置为True 会自动提交数据库中的变动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
# 实例化 SQLAlchemy 对象
db = SQLAlchemy(app)
2.定义模型
Flask-SQLAlchemy 创建的数据库实例为模型提供了一个基类以及一系列辅助类和辅助函数,可用于定义模型的结构。图 5-1 中的 roles 表和 users 表可定义为模型 Role 和 User,如示例 5-2 所示。
# 定义role 和 user 模型
class Role(db.Model):
# 定义在数据库中使用的表名 如果不写 会默认使用类名作为表名
__tablename__ = 'roles'
# db.Column类构造函数 构造 列
#参数1 是数据库列属性的类型 其余参数是配置选项
# flask 模型需要自己定义主键 不会自动生成 一般将id作为主键
# primary_key = True 默认也会将自增选项,非空选项也勾上
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
# 返回一个具有可读性的字符串模型 方便调试
def __repr__(self):
return '' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
def __repr__(self):
return '' % self.username
常用列类型
常用列选项
3.关系
关系型数据库使用关系把不同表中的行联系起来。
# 关系 外键
#在游戏中 一个角色(role)可以被多个用户(user)选择
# 但是一个用户只能有一个角色
class Role(db.Model):
# ...
# db.relationship() 参数1 是 模型(如果尚未定义,可以用字符串表示) backref参数向user模型添加一个role属性,从而定义反向关系
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
# db.ForeignKey('roles.id') 说明这列的值是roles表中的id值
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
大多数情况下,db.relationship() 都能自行找到关系中的外键,但有时却无法决定把哪一列作为外键。例如,如果 User 模型中有两个或以上的列定义为 Role 模型的外键,SQLAlchemy 就不知道该使用哪列。如果无法决定外键,你就要为 db.relationship() 提供额外参数,从而确定所用外键。
常用的关系选项
除了一对多之外,还有几种其他的关系类型。一对一关系可以用前面介绍的一对多关系表示,但调用 db.relationship() 时要把 uselist 设为 False,把“多”变成“一”。多对一关系也可使用一对多表示,对调两个表即可,或者把外键和 db.relationship() 都放在“多”这一侧。最复杂的关系类型是多对多,需要用到第三张表,这个表称为关系表。
2.数据库操作
1.创建表
(venv) $ python hello.py shell
>>> from hello import db
>>> db.create_all()
# db.create_all() 这个命令不会重新创建或者更新表,如果修改模型后将改动应用到数据库中 粗暴的方法就是删除旧表再重新创建
>>> db.drop_all() # 删除旧表
>>> db.create_all()
# 但是这样数据就全部被销毁了
2.插入行
# 创建一些角色和用户
>>> from hello import Role, User
# id 是 主键 由Flask-SQLAlchemy 管理 可以不用手动传入
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)
通过数据库会话管理对数据库所做的改动,在 Flask-SQLAlchemy 中,会话由 db.session表示。准备把对象写入数据库之前,先要将其添加到会话中:
>>> db.session.add(admin_role)
>>> db.session.add(mod_role)
>>> db.session.add(user_role)
>>> db.session.add(user_john)
>>> db.session.add(user_susan)
>>> db.session.add(user_david)
# 也可以简写成
>>> db.session.add_all([admin_role, mod_role, user_role,
user_john, user_susan, user_david])
# 调用commit提交会话
>>> db.session.commit()
# rollback 可以回滚 添加到数据库会话中的所有对象都会还原
>>>db.session.rollback()
数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误,整个会话都会失效。如果你始终把相关改动放在会话中提交,就能避免因部分更新导致的数据库不一致性。
3.修改行
在数据库会话上调用 add() 方法也能更新模型。我们继续在之前的 shell 会话中进行操作,下面这个例子把 "Admin" 角色重命名为 "Administrator":
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()
4.删除行
数据库会话还有个 delete() 方法。
>>> db.session.delete(mod_role)
# 删除,插入,更新都要提交数据库会话后才会执行
>>> db.session.commit()
5.查询行
Flask-SQLAlchemy 为每个模型类都提供了 query 对象。最基本的模型查询是取回对应表中的所有记录:
role.query.all()
# 返回一个列表 后面不能再进行链式查询
>>> Role.query.all()
[, ]
>>> User.query.all()
[, , ]
使用过滤器可以配置 query 对象进行更精确的数据库查询。下面这个例子查找角色为"User" 的所有用户:
query.filter_by() 与 query.filter() 的区别
总得来说 filter的功能比filter_by强大的多
#1.filter_by()
#filter_by用于查询简单的列名,不支持比较运算符。
filters = {’name': ‘fengyao', ‘age': 26}
User.query.filter_by(**filters).first()
#2.filter()
# 比filter_by的功能更强大,支持比较运算符,支持or_、in_等语法。
# 不过查询时 需要 以 模型名.列名 运算符 查询值 的格式
filters = {
User.name == ‘fengyao’,
User.age > 25
}
User.query.filter(**filters).first()
若要查看 SQLAlchemy 为查询生成的原生 SQL 查询语句,只需把 query 对象转换成字符串:
>>> str(User.query.filter_by(role=user_role))
'SELECT users.id AS users_id, users.username AS users_username,
users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'
filter_by() 等过滤器在 query 对象上调用,返回一个更精确的 query 对象。多个过滤器可以一起调用,直到获得所需结果。
常用的查询过滤器
在查询上应用指定的过滤器后,通过调用 all() 执行查询,以列表的形式返回结果。除了all() 之外,还有其他方法能触发查询执行。
常用的查询执行函数
6.在视图函数中操作数据库
数据库操作可以直接在视图函数中进行。
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
# 查找username为传入参数的 如果有则将该名字写入session中
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html',form=form, name=session.get('name'),known=session.get('known', False))
在这个修改后的版本中,提交表单后,程序会使用 filter_by() 查询过滤器在数据库中查找提交的名字。变量 known 被写入用户会话中,因此重定向之后,可以把数据传给模板,用来显示自定义的欢迎消息。
对应的模板. 这个模板使用 known 参数在欢迎消息中加入了第二行,从而对已知用户和新用户显示不同的内容。
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!
{% if not known %}
Pleased to meet you!
{% else %}
Happy to see you again!
{% endif %}
{{ wtf.quick_form(form) }}
{% endblock %}
7. 使用Flask-Migrate实现数据库迁移
1 创建迁移仓库
# 配置Flask-Migrete
from flask_migrate import Migrate, MigrateCommand
# ...
migrate = Migrate(app, db)
#
manager.add_command('db', MigrateCommand)
为了导出数据库迁移命令,Flask-Migrate 提供了一个 MigrateCommand 类,可附加到 FlaskScript的 manager 对象上。在这个例子,类使用 db 命令附加。
在迁移之前,先使用init子命令创建迁移仓库
# hello.py就是 那个启动文件(manage)
(venv) $ python hello.py db init
这个命令会创建migrations 文件夹,所有迁移脚本都存放其中。
2.创建迁移脚本
在 Alembic(flask开发人员编写的迁移数据库框架) 中,数据库迁移用迁移脚本表示。脚本中有两个函数,分别是 upgrade() 和downgrade()。upgrade() 函数把迁移中的改动应用到数据库中,downgrade() 函数则将改动删除。Alembic 具有添加和删除改动的能力,因此数据库可重设到修改历史的任意一点。
我们可以使用 revision 命令手动创建 Alembic 迁移,也可使用 migrate 命令自动创建。手动创建的迁移只是一个骨架,upgrade() 和 downgrade() 函数都是空的,开发者要使用Alembic 提供的 Operations 对象指令实现具体操作。自动创建的迁移会根据模型定义和数据库当前状态之间的差异生成 upgrade() 和 downgrade() 函数的内容。
# migrate 自动创建的迁移不一定总是正确
(venv) $ python hello.py db migrate -m "initial migration"
3.更新数据库
upgrade 命令把迁移应用到数据库中
(venv) $ python hello.py db upgrade
#对第一个迁移来说,其作用和调用 db.create_all() 方法一样。但在后续的迁移中,upgrade 命令能把改动应用到数据库中,且不影响其中保存的数据。