6、使用Flask-SQLAlchemy管理数据库和数据库的迁移
使用Flask-SQLAlchemy管理数据库
在视图函数中操作数据库
使用Flask-Migrate实现数据库的迁移
1、安装Flask-SQLAlchemy
2、hello.py配置数据库
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
manager = Manager(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
3、hello.py 定义Role和User模型
类变量__tablename__定义在数据库中使用的表名。如果没有定义,Flask-SQLAlchemy会使用一个默认的名字,但默认的表名没有遵守使用复数形式进行命名的约定,最好由我们自己指定表名。其余变量都是该模型的属性,被定义为db.Cloumn类的实例
#定义数据库模型 class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) def __repr__(self): return '<Role %r>' %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 'User %r' %self.username
4、关系
users = db.relation('User', backref='role')
添加到Role模型中的users属性代表这个关系的面向对象视角。对于一个Role类的实例,其users属性将返回与角色相关联的用户组成的列表。
db.relationship()第一个参数表明这个关系的另一端是哪个模型(类)。如果模型类尚未定义,可使用字符串形式指定。
db.relationship()第二个参数backref,将向User类中添加一个role属性,从而定义反向关系。这一属性可替代role_id访问Role模型,此时获取的是模型对象,而不是外键的值。
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
这句话是说,User类中添加了一个role_id变量,数据类型db.Integer,第二个参数指定外键是哪个表中哪个id
当表之间的关系是一对一的关系时,可添加userlist参数
db.Relationship('User',backref='role',uselist=False)
5、在shell中创建数据库
在shell中激活虚拟环境,运行python hello.py shell
我们要让 Flask-SQLAlchemy 根据模型类创建数据库。方法是使用 db.create_all()函数,执行完后,目录下会多一个data.sqlite的文件
6、生成data.sqlite文件后,在pycharm中需下载相应包进行支持,否则数据库不能用
如果没有下载相应文件,红框处会有下载提示,点击下载即可
6、插入行
创建一些角色和用户
from hello import Role, User 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)
此时新建对象的id属性并没有明确设定,因为主键是由 Flask-SQLAlchemy 管理的,现在这些对象只存在于python中,还未写入数据库,因此id未赋值
在 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])
提交到会话
db.session.commit()
roles表 users表
SELECT * FROM roles SELECT * FROM users
说明:
users表中的用户拥有唯一的角色,在users表中定义role_id并设置属性为外键,在roles表中给users表添加role属性,该属性可以替代users表中的role_id访问roles模型
user_john = User(username='john', role=admin_role) 可以看到可以通过role属性,访问了roles表中的id,并通过设置的关系,自动关联users表中role_id 和roles表中的id列
7、修改行
admin_role.name = 'Administrator' db.session.add(admin_role) db.session.commit()
roles表状态
8、删除行
db.session.delete(mod_role)
db.session.commit()
roles表状态
9、查询行
Flask-SQLAlchemy为每个模型类都提供了query对象,最基本的模型查询对象是取回对应表中的所有记录:
使用过滤器可以配置query对象进行更精确的数据库查询,下面是查找角色为“User”的所有用户
若要查看SQLAlchemy为查询生成的原生SQL查询语句,只需把query对象转换成字符串:
退出shell后,重新打开一个shell会话,就要从数据库中读取行,再重新创建python对象,下面是发起一个查询,加载名为“User”的用户角色
filter_by()等过滤器在query对象上调用,返回一个更精确的query对象。多个过滤器可以一起调用,知道获取结果
在视图函数中操作数据库
1、修改hello.py中的index方法
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
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被写入用户会话中,因此重定向后,可以把数据传给模板,用来显示自定义的欢迎消息。
2、修改templates/index.html
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf%} {% block title %}Flasky{% endblock %} {% block page_content %} <div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}</h1> {% if not known %} <p>Pleased to meet you!</p> {% else %} <p>Happy to see you again!</p> {% endif %} </div> {{ wtf.quick_form(form)}} {% endblock %}
3、集成python shell
每次启动shell都要导入数据库实例和模型,为避免重复,下面命令可让Flask-Script的shell命令自动导入特点的对象
from flask_script import Manager,Shell #为shell命令添加一个上下文 def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context))
在次启动时,自动导入了模型
使用Flask-Migrate实现数据库的迁移
1、安装Flask-Migrate
2、在hello.py中配置Flask-Migrate
from flask_migrate import Migrate, MigrateCommand app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite' app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True app.config["SECRET_KEY"] = "123456" bootstrap = Bootstrap(app) db = SQLAlchemy(app) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand)
3、在迁移前,使用init子命令创建迁移仓库
python hello.py db init
该命令会创建migrations文件夹,所有迁移脚本都会存放其中
执行完之后的目录结构
4、创建迁移脚本
在Alembic中,数据库迁移用迁移脚本表示,有两个函数:
upgrade() 把迁移中的改动应用到数据库中
downgrad() 将改动删除
数据库可以重设到修改历史的任意一点
迁移操作分为:
手动迁移,只是一个骨架,uograde()和downgrade()函数都是空的,开发者要使用Alembic提供的Operations对象指令实现具体操作
自动迁移,会根据模型定义和数据库当前状态之间的差异生成upgrade()和downgrade()函数的内容
migrate子命令用来自动创建迁移脚本:
python hello.py db migrate -m "initial migration"
5、更新数据库
检查并修正好迁移脚本后,可以使用 db upgrade 命令将迁移应用到数据库中:
python hello.py db uograde
生成需求文件
pip freeze >requirements.txt
在新的虚拟环境中创建完整副本
pip install -r requirements.txt