Flask中的数据库
Flask-SQLAlchemy
- 这个插件为流行的SQLAlchemy包做了一层封装以便在Flask中调用更方便,
ORM:
- 类似SQLAlchemy这样的包叫做Object Relational Mapper,简称ORM。
- ORM允许应用程序使用高级实体(如类,对象和方法)而不是表和SQL来管理数据库。 ORM的工作就是将高级操作转换成数据库命令.
Flask-Migrate
- 这个插件是Alembic的一个Flask封装,是SQLAlchemy的一个数据库迁移框架。
安装Flask-SQLAlchemy插件:
(venv) $ pip install flask-sqlalchemy
(venv) $ pip install flask-migrate
Flask-SQLAlchemy配置
SQLite数据库
- SQLite数据库是开发小型乃至中型应用最方便的选择,因为每个数据库都存储在磁盘上的单个文件中
# 配置文件添加两个新的配置项 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
Flask-SQLAlchemy插件从SQLALCHEMY_DATABASE_URI配置变量中获取应用的数据库的位置。 当回顾第三章可以发现,首先从环境变量获取配置变量,未获取到就使用默认值,这样做是一个好习惯。 本处,我从DATABASE_URL环境变量中获取数据库URL,如果没有定义,我将其配置为basedir变量表示的应用顶级目录下的一个名为app.db的文件路径。
SQLALCHEMY_TRACK_MODIFICATIONS配置项用于设置数据发生变更之后是否发送信号给应用,我不需要这项功能,因此将其设置为False。
# 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)
# db对象来表示数据库
migrate = Migrate(app, db)
# 数据库迁移引擎migrate
from app import routes, models
# models的模块,这个模块将会用来定义数据库结构。
用户表数据库
# app/models.py
from app import db
# User类继承自db.Model,它是Flask-SQLAlchemy中所有模型的基类
# 类将表的字段定义为类属性,字段被创建为db.Column类的实例
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)
- 该类的repr方法用于在调试时打印用户实例。在下面的Python交互式会话中你可以看到repr()方法的运行情况:
>>> from app.models import User
>>> u = User(username='susan', email='susan@example.com')
>>> u
<User susan>
创建数据库迁移数据库
- flask db init来创建microblog的迁移存储库:
(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的新目录。该目录中包含一个名为versions的子目录以及若干文件.
第一次数据库迁移
- flask db migrate子命令生成这些自动迁移:
(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
- 有两个函数叫upgrade()和downgrade()。 upgrade()函数应用迁移,downgrade()函数回滚迁移。
- flask db migrate命令不会对数据库进行任何更改,只会生成迁移脚本。 要将更改应用到数据库,必须使用flask db upgrade命令。
(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
- 因为本应用使用SQLite,所以upgrade命令检测到数据库不存在时,会创建它(在这个命令完成之后,你会注意到一个名为app.db的文件,即SQLite数据库)。 在使用类似MySQL和PostgreSQL的数据库服务时,必须在运行upgrade之前在数据库服务器上创建数据库。
数据库关系
— 修改后的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):
# 新的“Post”类表示用户发表的动态
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
# timestamp字段将被编入索引,如果你想按时间顺序检索用户动态
"""
我还为其添加了一个default参数,并传入了datetime.utcnow函数。
当你将一个函数作为默认值传入后,SQLAlchemy会将该字段设置为调用该函数的值(请注意,在utcnow之后我没有包含(),所以我传递函数本身,而不是调用它的结果
"""
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post {}>'.format(self.body)
- 在服务应用中使用UTC日期和时间是推荐做法。 这可以确保你使用统一的时间戳,无论用户位于何处,这些时间戳会在显示时转换为用户的当地时间。
- 迁移数据库
(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