《Flask Web Development》第5章 數(shù)據(jù)庫(kù)

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/database

    hostname對(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)容。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末彼棍,一起剝皮案震驚了整個(gè)濱河市灭忠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌座硕,老刑警劉巖弛作,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異坎吻,居然都是意外死亡缆蝉,警方通過(guò)查閱死者的電腦和手機(jī)宇葱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門瘦真,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人黍瞧,你說(shuō)我怎么就攤上這事诸尽。” “怎么了印颤?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵您机,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)际看,這世上最難降的妖魔是什么咸产? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮仲闽,結(jié)果婚禮上脑溢,老公的妹妹穿的比我還像新娘。我一直安慰自己赖欣,他們只是感情好屑彻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顶吮,像睡著了一般社牲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悴了,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天搏恤,我揣著相機(jī)與錄音,去河邊找鬼让禀。 笑死挑社,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巡揍。 我是一名探鬼主播痛阻,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腮敌!你這毒婦竟也來(lái)了阱当?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糜工,失蹤者是張志新(化名)和其女友劉穎弊添,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌木,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡油坝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刨裆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澈圈。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帆啃,靈堂內(nèi)的尸體忽然破棺而出瞬女,到底是詐尸還是另有隱情,我是刑警寧澤努潘,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布诽偷,位于F島的核電站坤学,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏报慕。R本人自食惡果不足惜深浮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望眠冈。 院中可真熱鬧略号,春花似錦、人聲如沸洋闽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诫舅。三九已至羽利,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刊懈,已是汗流浹背这弧。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虚汛,地道東北人匾浪。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像卷哩,于是被迫代替她去往敵國(guó)和親蛋辈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停将谊,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,160評(píng)論 22 257
  • 第五章 數(shù)據(jù)庫(kù) 序:什么是數(shù)據(jù)庫(kù) 數(shù)據(jù)庫(kù)按規(guī)則保存程序數(shù)據(jù)冷溶,程序發(fā)起查詢?nèi)』財(cái)?shù)據(jù)。Web 程序最常使用基于關(guān)系模型...
    科幻經(jīng)典閱讀 827評(píng)論 0 1
  • chapter 2 - chapter 3 - chapter 4 - 源碼 概念剖析-flask數(shù)據(jù)庫(kù)操作數(shù)據(jù)...
    呆呆的張先生閱讀 828評(píng)論 0 1
  • 外公過(guò)生尊浓,我昨天特地趕回來(lái)為他慶生逞频。 外公退休前是位鄉(xiāng)村教師,行事簡(jiǎn)樸栋齿。這次請(qǐng)客苗胀,也不例外。在老屋幾步開外的親戚家...
    velynneji閱讀 264評(píng)論 0 1
  • 從武漢到重慶瓦堵,14個(gè)小時(shí)的硬座讓我的小腿腫得看不見腳踝基协,肚子也不斷向外脹氣,縱然重慶再讓我意外也提不起精神了谷丸。...
    _____兀閱讀 289評(píng)論 0 0