SQL Databases
特點(diǎn)是什么摸袁?(得空補(bǔ)充)
NoSQL Databases
特點(diǎn)是什么靶擦?(得空補(bǔ)充)
SQL or NoSQL?
相比各自的優(yōu)勢(shì)是什么?(得空補(bǔ)充)
Python Database Frameworks
你可以使用哪些數(shù)據(jù)庫(kù)
不管是否開源的數(shù)據(jù)庫(kù)桥狡,F(xiàn)lask都有對(duì)應(yīng)數(shù)據(jù)庫(kù)引擎的package比如:MySQL缨伊、 Postgres、 SQLite蘸秘、 Redis官卡、MongoDB 或 CouchDB。你還可以使用什么
如果這些還不夠醋虏,還有一些數(shù)據(jù)庫(kù)抽象層package比如SQLAlchemy寻咒、 MongoEngine能讓你在更高的層次操作數(shù)據(jù)(以對(duì)象形式)而非以table、document颈嚼、query languages的形式毛秘。-
選擇數(shù)據(jù)庫(kù)有哪些參考依據(jù)
- 易用性:SQL Vs NoSQL, 后者當(dāng)然完勝了
- 效率:后者在對(duì)象到數(shù)據(jù)庫(kù)模型轉(zhuǎn)化中會(huì)有一定開銷,但這可以忽略不計(jì),反過(guò)來(lái)叫挟,后者對(duì)生成率的促進(jìn)遠(yuǎn)超過(guò)造成的開銷艰匙。
- 可移植性:盡管很多數(shù)據(jù)庫(kù)抽象層只提供對(duì)一種數(shù)據(jù)庫(kù)的支持,但是有的更高層次的數(shù)據(jù)庫(kù)抽象層幾乎支持所有的數(shù)據(jù)庫(kù)抹恳,比如:SQLAlchemy ORM员凝。
- Flask集成:如果能以Flask的extension的形式存在意味著可以省去很多手寫的代碼。
本書選擇的數(shù)據(jù)庫(kù)
綜上奋献,本書選擇Flask-SQLAlchemy(作為SQLAlchemy的擴(kuò)展)作為數(shù)據(jù)庫(kù)工具健霹。
使用Flask-SQLAlchemy進(jìn)行數(shù)據(jù)庫(kù)管理
Flask-SQLAlchemy是一個(gè)使用了SQLAlchemy的擴(kuò)展。 SQLAlchemy 是一個(gè)強(qiáng)大的關(guān)系型數(shù)據(jù)庫(kù)框架秽荞,它能夠支持多種數(shù)據(jù)庫(kù)并提供了高層的ORM和底層的原生數(shù)據(jù)庫(kù)操作骤公。
- 安裝
(venv) $ pip install flask-sqlalchemy
-
數(shù)據(jù)庫(kù)URL
在Flask-SQLAlchemy中,數(shù)據(jù)庫(kù)被表示為一個(gè)URL扬跋,如下所示:MySQL mysql://username:password@hostname/database
Postgres postgresql://username:password@hostname/database
SQLite (Unix) sqlite:////absolute/path/to/database
SQLite (Windows) sqlite:///c:/absolute/path/to/databasehostname對(duì)應(yīng)一臺(tái)主機(jī)阶捆,username和password好理解,對(duì)于sqlite數(shù)據(jù)庫(kù)是沒(méi)有用戶名密碼的钦听,所以它只是目錄下的一個(gè)文件而已洒试。
配置
數(shù)據(jù)庫(kù)URL被在配置在: SQLALCHEMY_DATABASE_URI,還有另一個(gè)重要的屬性被配置在SQLALCHEMY_COMMIT_ON_TEARDOWN( 用來(lái)在每次請(qǐng)求結(jié)束時(shí)候提交數(shù)據(jù)庫(kù)改動(dòng)朴上,通常設(shè)置為True)垒棋。實(shí)例
如下是一個(gè)配置SQLite數(shù)據(jù)庫(kù)的例子, db實(shí)例化了一個(gè)SQLAlchemy對(duì)象并提供了所有Flask-SQLAlchemy具備的功能:
import os
#..
from flask.ext.sqlalchemy import SQLAlchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
Model定義
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
類變量 __tablename__ 定義了表名(盡管默認(rèn)會(huì)有名字,但是是非復(fù)數(shù)形式的不太好)痪宰,所有的屬性都定義為db.Column的實(shí)例對(duì)象叼架,db.Column的第一個(gè)參數(shù)是類別第二個(gè)參數(shù)是可選配置參數(shù)(所有table都要有primary key),__repr__()方法是為了方便調(diào)試和測(cè)試用衣撬。
Relationships
如下示例展示了一個(gè)一對(duì)多的關(guān)系(對(duì)于配置的理解不深先照樣寫吧乖订!),該書附錄了一張表格列出了db.relationship常用的配置參數(shù)說(shuō)明具练,需要時(shí)參考:
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
Database操作
學(xué)習(xí)這些數(shù)據(jù)庫(kù)操作的的最好辦法是在Python Shell中乍构,如下幾個(gè)部分將開始學(xué)習(xí)最常見的數(shù)據(jù)庫(kù)操作(本章前面的構(gòu)建model的代碼應(yīng)用到hello.py中):
# shell方式需要安裝flask-script,create_all會(huì)創(chuàng)建所有的model
(venv) $ python hello.py shell
>>> from hello import db
>>> db.create_all()
#重新設(shè)計(jì)表結(jié)構(gòu)以后需要drop掉之前的表
>>> db.drop_all()
>>> db.create_all()
>>> 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)
# 因?yàn)闆](méi)有commit所有的對(duì)象都還沒(méi)有id
>>> print(admin_role.id) None
>>> print(mod_role.id) None
>>> print(user_role.id) None
# 用db.session管理對(duì)象的持續(xù)化
>>> 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.commit()
# 數(shù)據(jù)已經(jīng)提交帶數(shù)據(jù)庫(kù)
>>> print(admin_role.id) 1
>>> print(mod_role.id) 2
>>> print(user_role.id) 3
# 修改屬性
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()
# 刪除
>>> db.session.delete(mod_role)
>>> db.session.commit()
# 查詢
>>> Role.query.all()
[<Role u'Administrator'>, <Role u'User'>]
>>> User.query.all()
[<User u'john'>, <User u'susan'>, <User u'david'>]
# 過(guò)濾器
>>> User.query.filter_by(role=user_role).all()
[<User u'susan'>, <User u'david'>]
# 查詢方法和過(guò)濾器是多種多樣的,該書列出了兩個(gè)表格可供查詢扛点,參考page61.
# 還可以獲取到原生的查詢語(yǔ)句
>>> str(User.query.filter_by(role=user_role))
'SELECT users.id AS users_id, users.username AS users_username,
users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'
# 注意哥遮,關(guān)閉了shell窗口以后,意味著你要重新import db陵究、Role,重新構(gòu)建之前定義過(guò)的對(duì)象眠饮,如下:
>>> from hello import db
>>> from hello import Role
>>> user_role = Role.query.filter_by(name='User').first()
# 關(guān)聯(lián)查詢
>>> users = user_role.users
>>> users
[<User u'susan'>, <User u'david'>]
>>> users[0].role
<Role u'User'>
# 如上查詢,如果想要應(yīng)用filter是不行的铜邮。因?yàn)閡ser_role.usres已經(jīng)調(diào)用了all()方法君仆,要使用filter需要在py中進(jìn)行修改:
class Role(db.Model):
# ...
users = db.relationship('User', backref='role', lazy='dynamic')
# ...
>>> user_role.users.order_by(User.username).all()
[<User u'david'>, <User u'susan'>]
>>> user_role.users.count()
2
Python Shell集成Model
如果每次打開Shell都要手動(dòng)導(dǎo)入未免太繁瑣了,F(xiàn)lask-Script提供了配置可以自動(dòng)導(dǎo)入對(duì)象:
from flask.ext.script import 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))
在View Functions中操作數(shù)據(jù)庫(kù)
將數(shù)據(jù)庫(kù)操作應(yīng)用到View Function中,如下是一個(gè)例子:
index.py
@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))
templates/index.html
{% extends "commonBase.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 %}
使用Flask-Migrate來(lái)做數(shù)據(jù)庫(kù)的Migrations
開發(fā)進(jìn)行到一定階段返咱,你會(huì)發(fā)現(xiàn)model的結(jié)構(gòu)需要發(fā)生改變钥庇,F(xiàn)lask-SQLAlchemy從Model來(lái)構(gòu)建數(shù)據(jù)庫(kù)表結(jié)構(gòu)只會(huì)發(fā)生在以前表不存在的時(shí)候,當(dāng)然你也可以不管數(shù)據(jù)丟失先刪除數(shù)據(jù)庫(kù)咖摹。
更好的做法是使用數(shù)據(jù)庫(kù)遷移框架评姨,就像代碼能夠進(jìn)行版本控制一樣,一個(gè)數(shù)據(jù)庫(kù)遷移框架能夠跟蹤數(shù)據(jù)庫(kù)的變化萤晴,并且漸進(jìn)的應(yīng)用數(shù)據(jù)庫(kù)的改變吐句。
SQLAlchemy的開發(fā)者寫了一個(gè)名叫Alembic的框架,但是我們并不打算直接使用它店读,而是使用Flask-Migrate extension擴(kuò)展來(lái)和Flask-Script集成嗦枢,全部通過(guò)命令行來(lái)達(dá)到目的。
Creating a Migration Repository
(venv) $ pip install flask-migrate
如下展示而來(lái)該擴(kuò)展的配置方式:
from flask.ext.migrate import Migrate, MigrateCommand
# ...
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
使用db的子命令來(lái)構(gòu)建一個(gè)資源庫(kù):
(venv) $ python index.py db init
Creating a Migration Script
Alembic migrations能夠有手動(dòng)和自動(dòng)兩種模式可用屯断。
手動(dòng)的migration要?jiǎng)?chuàng)建空的工具方法upgrade()和downgrade()文虏,自動(dòng)migration會(huì)自動(dòng)查找當(dāng)前數(shù)據(jù)庫(kù)和model definitions的不同之處來(lái)完成upgrade()和downgrade()。
一個(gè)自動(dòng) migration 的例子:
(venv) $ python index.py db migrate -m "initial migration"
Upgrading the Database
一旦migration完成殖演,你就可以通過(guò)db upgrade 來(lái)更新數(shù)據(jù)庫(kù)了氧秘,你可以把data.sqlite刪除以后再執(zhí)行命令。
(venv) $ python hello.py db upgrade
貫穿全書都會(huì)圍繞migration來(lái)推進(jìn)趴久,本書只是簡(jiǎn)單介紹丸相,后面有更詳細(xì)的內(nèi)容。