Flask學習之四 數據庫


英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database

中文翻譯地址:http://www.pythondoc.com/flask-mega-tutorial/database.html

開源中國社區:www.oschina.net/translate/the-flask-mega-tutorial-part-iv-database

 

一、Flask中的數據庫

教程:將使用 Flask-SQLAlchemy 擴展來管理我們應用程序的數據。這個擴展封裝了 SQLAlchemy 項目,這是一個 對象關系映射器 或者 ORM。

大多數數據庫教程會涉及到創建和使用一個數據庫,但沒有充分講述隨着應用程序擴大更新數據庫的問題。通常情況下,每次你需要進行更新,你最終不得不刪除舊 的數據庫和創建一個新的數據庫,並且失去了所有的數據。如果數據不能容易地被重新創建,你可能會被迫自己編寫導出和導入腳本。

我們將使用 SQLAlchemy-migrate 來跟蹤數據庫的更新。它只是在開始建立數據庫的時候多花費些工作,這只是很小的代價,以后就再不用擔心人工數據遷移了。

 

二、配置

教程采用的數據庫是sqlite 數據庫。sqlite 數據庫是小型應用的最方便的選擇,每一個數據庫都是存儲在單個文件里。

有許多新的配置項需要添加到配置文件中(文件 config.py):

import os
basedir = os.path.abspath(os.path.dirname(__file__))

SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')

SQLALCHEMY_DATABASE_URI 是 Flask-SQLAlchemy 擴展需要的。這是我們數據庫文件的路徑。

SQLALCHEMY_MIGRATE_REPO 是文件夾,我們將會把 SQLAlchemy-migrate 數據文件存儲在這里。

 

當我們初始化應用程序的時候,我們也必須初始化數據庫。修改初始化文件(文件 app/__init__.py):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)

from app import views, models

這里的改變有兩個。一是創建了一個 db 對象,這是我們的數據庫,接着導入一個新的模塊,叫做 models。接下來我們將編寫 models。

 

三、數據庫模型

我假設大家能學過數據庫。

用戶表如下圖:

_images/2.png

表中共有4個字段。

id:主鍵

nickname :字符串,並且指定了最大的長度以便數據庫可以優化空間占用。

email :字符串,並且指定了最大的長度以便數據庫可以優化空間占用。

role :整型,表示哪個用戶是管理員,哪個不是。

把它轉換成代碼(文件 app/models.py):

from app import db

ROLE_USER = 0
ROLE_ADMIN = 1

class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nickname = db.Column(db.String(64), index = True, unique = True)
    email = db.Column(db.String(120), index = True, unique = True)
    role = db.Column(db.SmallInteger, default = ROLE_USER)

    def __repr__(self):
        return '<User %r>' % (self.nickname)

User類把我們剛剛創建的幾個字段定義為類變量。字段使用db.Column類創建實例,字段的類型作為參數,另外還提供一些其他可選參數。例如,標識字段唯一性和索引的參數.

__repr__方法告訴Python如何打印class對象,方便我們調試使用。

 

四、創建數據庫

教程:配置以及模型都已經到位了,是時候准備創建數據庫文件。SQLAlchemy-migrate 包自帶命令行和 APIs,這些 APIs 以一種將來允許容易升級的方式來創建數據庫。我發現命令行使用起來比較別扭,因此我們自己編寫一些 Python 腳本來調用遷移的 APIs。

創建數據庫的腳本(文件 db_create.py):

#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
from app import db
import os.path
db.create_all()
if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
    api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
    api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
else:
    api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))

這個文件放在根目錄下。

注意這個腳本是完全通用的,所有的應用路徑名都是從配置文件讀取的。當你用在自己的項目時,你可以把腳本拷貝到你app`s目錄下就能正常使用了。

 

創建數據庫:

chmod a+x db_create.py
./db_create.py

運行這條命令之后,文件夾中就多了一個新的app.db文件,這是個支持遷移的空sqlite數據庫。同時也會生成一個帶有幾個文件的 db_repository目錄,這是SQLAlchemy-migrate存儲數據庫文件的地方,注意如果數據庫已存在它就不會再重新生成了。這將幫助 我們在丟失了現有的數據庫后,再次自動創建出來。

 

五、第一次遷移

既然已經定義好了model,也把它和數據庫做了關聯,接下來初次嘗試下做一個改變應用數據庫結構的一次遷移,這將幫助我們從一個空的數據庫變成一個可以存儲users信息的數據庫。

首先需要一個遷移腳本(文件 db_migrate.py):

#!flask/bin/python
import imp
from migrate.versioning import api
from app import db
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1)
tmp_module = imp.new_module('old_model')
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
exec old_model in tmp_module.__dict__
script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata)
open(migration, "wt").write(script)
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print 'New migration saved as ' + migration
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))

腳本代碼略長,慢慢看。

教程:SQLAlchemy-migrate通過對比數據庫的結構(從app.db文件讀取)和models結構(從app/models.py文件讀取)的方 式來創建遷移任務,兩者之間的差異將作為一個遷移腳本記錄在遷移庫中,遷移腳本知道如何應用或者撤銷一次遷移,所以它可以方便的升級或者降級一個數據庫的 格式。

然而在使用上面的腳本自動地完成遷移的時候也不是沒有問題的,我見過有時候它很難識別新老格式的變化。為了讓 SQLAlchemy-migrate 容易地識別出變化,我絕不會重命名存在的字段,我僅限於增加或者刪除模型或者字段,或者改變已存在字段的類型。當然我一直會檢查生成的遷移腳本,確保它是 正確。

毋庸置疑你不應該在沒有備份下去嘗試遷移數據庫。當然也不能在生產環境下直接運行遷移腳本,必須在開發環境下確保遷移運轉正常。

然后,執行吧:

chmod a+x db_migrate.py
./db_migrate.py

 

 

六、數據庫升級和回退

假設你有一個應用程序在開發機器上,同時有一個拷貝部署在到線上的生產機器上。在下一個版本中,你的數據模型有一個變化,比如新增了一個表。如果沒有遷移腳本,你可能必須要琢磨着如何修改數據庫格式在開發和生產機器上,這會花費很大的工作。

如果有數據庫遷移的支持,當你准備發布新版的時候,你只需要錄制一個新的遷移,拷貝遷移腳本到生產服務器上接着運行腳本,所有事情就完成了。數據庫升級也只需要一點 Python 腳本(文件 db_upgrade.py):

#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))

當你運行上述腳本的時候,數據庫將會升級到最新版本。

通常情況下,沒有必要把數據庫降低到舊版本,但是,SQLAlchemy-migrate 支持這么做(文件 db_downgrade.py):

#!flask/bin/python
from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1)
print 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))

這個腳本會回退數據庫一個版本。運行多次則可以回退多個版本。

 

七、數據庫關系

學過數據庫應該知道數據庫關系指的是什么,我假設大家能看到下面的圖:

_images/3.png

修改(app/models.py):

from app import db

ROLE_USER = 0
ROLE_ADMIN = 1

class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nickname = db.Column(db.String(64), unique = True)
    email = db.Column(db.String(120), unique = True)
    role = db.Column(db.SmallInteger, default = ROLE_USER)
    posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')

    def __repr__(self):
        return '<User %r>' % (self.nickname)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Post %r>' % (self.body)

我們添加了一個 Post 類,這是用來表示用戶編寫的 blog。在 Post 類中的 user_id 字段初始化成外鍵,因此 Flask-SQLAlchemy 知道這個字段是連接到用戶上。

值得注意的是我們已經在 User 類中添加一個新的字段稱為 posts,它是被構建成一個 db.relationship 字段。這並不是一個實際的數據庫字段,因此是不會出現在上面的圖中。對於一個一對多的關系,db.relationship 字段通常是定義在“一”這一邊。在這種關系下,我們得到一個 user.posts 成員,它給出一個用戶所有的 blog。

運行遷移腳本:

./db_migrate.py

 

 

八、測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM