Flask-SQLAlchemy
和 Flask-Migrate
注: SQLite
is used (no need to run a database server like MySQL and PostgreSQL), each
database is stored in a single file on disk
(venv) $ pip install flask-sqlalchemy
This extension is a Flask wrapper for Alembic, a database migration framework for SQLAlchemy.
(venv) $ pip install flask-migrate
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_TRACK_MODIFICATIONS:to signal the application every time a change is about to be made in the database.
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
注: bottom imports 中的 models
, 将会定义 db 的结构 (structure or schema)。
注:可以用WWW SQL Designer 在线进行db designes,并能导出 SQL 脚本 (wiki)。
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)
其中,
db.relationship('Post', backref='author', lazy='dynamic')
定义virtual field 的 posts时,同时定义了Post 的 virtual field,即 author (亦不在 真实的 db post表 中显示)。User.posts
, Post.author
;对应各自class的object,亦可调用)。db.relationship()
中,用的是 model class的名字(如: ‘Post’), 而db.ForeignKey()
中,用的是model class对应的 db table 名 (如 user.id
中的 user)。__tablename__
,则model class的 db table 名 采用 “snake case”方式 . For the User model above, the corresponding table in the database will be named user. For a AddressAndPhone model class, the table would be named address_and_phone.
flask run
, a sub-command native to Flask. flask db
, a sub-command added by Flask-Migrate to manage everything related to db migrations. 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.
注意: flask 的命令,依赖 环境变量 FLASK_APP ,以此判断 Flask application的位置 (for this application, FLASK_APP=microblog.py)。
(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` is optional, it adds a short descriptive text to the migration.
(venv) $ 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
(venv) $ flask db downgrade
flask shell
, to complete pre-imports, so we can start a Python interpreter in the context of the application, without having to repeat some imports every time we want to test things out in a Python shell.from app import app, db
from app.models import User, Post
@app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User, 'Post': Post}
flask shell
时,激活注册的函数,并将返回的 items 注册到 shell session 中. (venv) $ flask shell
>>> db
<SQLAlchemy engine=sqlite:////Users/migu7781/Documents/dev/flask/microblog2/app.db>
>>> User
<class 'app.models.User'>
>>> Post
<class 'app.models.Post'>
注:since Flask 1.0 there is an easy way to make Flask remember the FLASK_APP environment variable, so you do not need to export FLASK_APP=microblog.py
each time in virtual environment to get access to “db”, “User” and “Post” in shell.
Find info about the .flaskenv file in Flask documentation.