Flask中的数据库
Flask并不原生支持数据库,而是通过Python包以及Flask数据库插件。
数据库分为关系型数据库和非关系型数据库,这里我们使用关系型数据库。我们使用Flask-SQLAlchemy插件,它是SQLAlchemy的Flask插件包,基于对象关系映射ORM。SQLAlchemy支持很多数据引擎,包括MySSQL、PostgreSQL、SQLite等。通过下面的命令安装Flask-SQLAlchemy插件:
(venv) $ pip install flask-sqlalchemy
数据迁移
大部分数据库教程都是关于创建数据库,使用数据库的,很少有讲解对已有的数据库进行升级改造的。关系型数据库如果需要改变结构,需要进行数据迁移,这需要Flask-Migrate,一个SQLAlchemy的迁移工具。安装方式:
(venv) $ pip install flask-migrate
Flask-SQLAlchemy配置
这里我用了SQLite数据库,只需要一个本地文件,而不用安装MySQL数据库。我们在config.py中增加配置如下:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
# ...
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI
指定了数据库文件的存储位置,SQLALCHEMY_TRACK_MODIFICATIONS
屏蔽了不需要的Flask-SQLAlchemy的数据够新通知特性。
数据库实例
在应用中,数据库用实例表示,我们在app/init.py文件中进行实例化:
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app) # 数据库实例
migrate = Migrate(app, db) # 数据迁移引擎实例化
from app import routes, models # models定义数据库结构,对应下面新建的models.py。
数据库模型
借助SQLAlchemy数据在应用中表示成不同的类,SQLAlchemy负责将这些类表达成表。用户表的设计如下:
出于安全考虑,这里并没有明文存储用户密码。
新建app/models.py用以管理数据库,内热如下:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True) # 数据列
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
def __repr__(self): # 字符串显示方法
return '<User {}>'.format(self.username)
创建数据迁移仓库
如果需要对数据库的结构进行改变,需用上数据迁移工具。运行flask db init
命令,可以自动创建当前版本的数据迁移脚本。
(venv) $ flask db init
Creating directory /home/miguel/microblog/migrations ... done
Creating directory /home/miguel/microblog/migrations/versions ... done
Generating /home/miguel/microblog/migrations/alembic.ini ... done
Generating /home/miguel/microblog/migrations/env.py ... done
Generating /home/miguel/microblog/migrations/README ... done
Generating /home/miguel/microblog/migrations/script.py.mako ... done
Please edit configuration/connection/logging settings in
'/home/miguel/microblog/migrations/alembic.ini' before proceeding.
在项目目录中,增加了一个migrations
的文件夹,其中的文件应加入git进行跟踪。
数据迁移
有了数据迁移仓库就有了数据迁移脚本,我们可以进行第一次数据迁移,即创建用户数据表,这有两种方法:自动方法和手动方法。对于自动方法,Alembic会比较数据模型和数据库内的数据表,在数据迁移脚本中增加数据迁移命令。当前没有数据表,所以在数据迁移脚本中会增加在数据库中创建数据表的命令。修改数据迁移脚本命令如下:
(venv) $ flask db migrate -m "users table"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'user'
INFO [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']'
INFO [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']'
Generating /home/miguel/microblog/migrations/versions/e517276bb1c2_users_table.py ... done
命令中的-m "users table"
是可选的,起到注释作用。这条命令只是产生了一个py文件,用来进行数据迁移,并没有修改数据库。要修改数据库,需要运行如下命令:
(venv) $ flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> e517276bb1c2, users table
如果你使用了Mysql或者PostgreSQL,在运行flask db upgrade
之前应该先创建user表。Flask-SQLAlchemy会根据数据类的名称自动转换和创建数据表名称,日如User
类编程user表,AddressAndPhone类变成address_and_phone表。如果需要另选表名称,可以在数据类中增加__tablename__
参数。
数据库升级和降级
在实际工作中可能存在一个开发环境(本机)和一个在线环境(服务器)。如果你开发的新版应用需要修改表结构,那么你运行flask db migrate
可以自动产生数据迁移的命令。在git中更新后,在线环境直接运行flask db upgrade
就能同步升级数据库。
如果你发现新部署的应用有问题,可以用flask db downgrade
回退到上一个版本。
数据表关系
关系型数据库的表之间存在联系,比如博客中的文章需要记录是谁发布的,或者查询一个用户发了哪些帖子。我们增加一个post
表用来记录帖子。
帖子表中的user_id
是一个外键,用来和用户表进行连接,这种连接是一种一对多的连接。我们修改app/models.py如下:
from datetime import datetime
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')# 新增一个数据视图
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow) # 默认值传递一个函数
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 外键
def __repr__(self):
return '<Post {}>'.format(self.body)
在更新了应用中的数据模型后,需要产生新的数据迁移脚本:
(venv) $ flask db migrate -m "posts table"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'post'
INFO [alembic.autogenerate.compare] Detected added index 'ix_post_timestamp' on '['timestamp']'
Generating /home/miguel/microblog/migrations/versions/780739b227a7_posts_table.py ... done
然后执行数据迁移脚本,实际修改数据:
(venv) $ flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade e517276bb1c2 -> 780739b227a7, posts table
记得把这些数据迁移脚本加入git。