小引
- 引入兩個擴展:
Flask-SQLAlchemy
和Flask-Migrate
- 引入 Database Migration 的概念
- 利用SQLAlchemy,完成 db Model
- 引入 shell context 的概念
注: SQLite
is used (no need to run a database server like MySQL and PostgreSQL), each
database is stored in a single file on disk
Extensions 安裝
- Flask-SQLAlchemy
(venv) $ pip install flask-sqlalchemy
- Flask-Migrate
This extension is a Flask wrapper for Alembic, a database migration framework for SQLAlchemy.
(venv) $ pip install flask-migrate
Configuration of Flask-SQLAlchemy
- Flask_microblog / config.py (Configuration of Flask-SQLAlchemy)
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.
- app / _init_.py (Initialization of Flask-SQLAlchemy & Flask-Migrate)
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)。
Database Models
注:可以用WWW SQL Designer 在線進行db designes,並能導出 SQL 腳本 (wiki)。
- 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)
其中,
- Post 中的 user_id,爲 User 中的 id 外鍵。
- User 中的 posts 屬性,爲 User 的 virtual field,真實的 db user表 中不會顯示。
- 類似的,User 中 通過
db.relationship('Post', backref='author', lazy='dynamic')
定義virtual field 的 posts時,同時定義了Post 的 virtual field,即 author (亦不在 真實的 db post表 中顯示)。 - 虛擬的virtual field,雖不顯示,但可正常調用(
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.
Database Relationships
注: “one” to “many” 類型, {“one”: User, “many”; Post} ![這裏寫圖片描述](https://img-blog.csdn.net/20180606230745514?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0t1bmdyZXll/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)Creating Migration Repository
注:Flask-Migrate exposes its commands through the flask command.flask run
, a sub-command native to Flask.flask db
, a sub-command added by Flask-Migrate to manage everything related to db migrations.
Create the migration repository for microblog by flask db init
:
命令執行位置,同 flask run(即設定環境變量 `export FLASK_APP = microblog.py` 的項目根目錄下)。
(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)。
The First Database Migration
`(venv) $ flask db migrate -m “users table”`(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.
Database Upgrade and Downgrade
(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
When to undo the last migration, we can downgrade the database, delete the migration script, and then generate a new one to replace it.
Shell Context
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}
- 通過@app.shell_context_processor, 將函數註冊 爲 shell context function.
- 運行命令
flask shell
時,激活註冊的函數,並將返回的 items 註冊到 shell session 中. - 返回的是 dict 而不是 list,是因爲需要通過 keys 給每個 item 賦名稱,以便在 shell session 中調用。
- After adding the shell context processor function, we can work with db entities without having to import them:
(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.