Python的Flask架構與資料庫連接的教程

來源:互聯網
上載者:User
命令列方式運行Python指令碼

在這個章節中,我們將寫一些簡單的資料庫管理指令碼。在此之前讓我們來複習一下如何通過命令列方式執行Python指令碼.

如果Linux 或者OS X的作業系統,需要有執行指令碼的許可權。例如:

chmod a+x script.py

該指令碼有個指向使用解譯器的命令列。再指令碼賦予執行許可權後就可以通過命令列執行,就像這樣: like this:

./script.py 

然而,在Windows系統上這樣做是不行的,你必須提供Python解譯器作為必選參數,如:

代碼如下:

flask/Scripts/python script.py

為了避免Python解譯器路徑輸入出錯,你可以將你的檔案夾microoblog/flask/Scripts添加到系統路徑,確保能正常顯示Python解譯器。

從現在開始,在Linux/OS X上的語句簡潔。如果你使用Windows系統請記得轉換語句。

在Flask使用資料庫

我們將使用Flask-SQLAlchemy 的擴充來管理資料庫。由SQLAlchemy項目提供的,已封裝了關聯性物件映射(ORM)的一個外掛程式。

ORMs允許資料庫程式用對象的方式替代表和SQL語句。物件導向的操作被ORM轉化為資料庫命令。這樣就意味著,不用sql語句,讓Flask-SQLAlchemy為我們執行sql語句。

遷移

大多數資料庫教程都覆蓋了建立和使用一個資料庫的方法,但是沒有充分解決當應用程式擴充時資料庫更新的問題。通常,你會刪除舊的資料庫,然後再建立一個新的資料庫來達到更新的效果,這樣就丟失了所有的資料。如果這些資料建立起來很費勁,那麼我們不得不寫匯入匯出的指令碼了。

幸運的是,我們有了更好的方案.

我們現在可以使用SQLAlchemy-migrate做資料庫遷移的更新了,雖然它增加了資料庫啟動時的負擔,但這點小小的代價還是值得的,畢竟我們不用擔心手動遷移資料庫的問題了。

理論學習完畢,我們開始吧!

配置

我們的小程式使用sqlite資料庫。sqlite是小程式資料庫的最佳選擇,一個可以以單檔案儲存體的資料庫。

在我們的設定檔中添加新的配置項 (fileconfig.py):

import osbasedir = 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是the Flask-SQLAlchemy必需的擴充。這是我們的資料庫檔案的路徑。

SQLALCHEMY_MIGRATE_REPO 是用來儲存SQLAlchemy-migrate資料庫檔案的檔案夾。


最後,初始化應用的時候也需要初始化資料庫。這裡是升級後的init檔案(fileapp/__init):

from flask import Flaskfrom flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__)app.config.from_object('config')db = SQLAlchemy(app) from app import views, models

注意產生的指令碼已改動2個地方。我們現在開始建立資料庫的adb對象,引用新的模組。馬上來寫這個模組。

資料庫模型

我們在資料庫儲存的資料通過資料庫model層被映射為一些類裡面的對象,ORM層將根據類對象映射到資料庫對應的欄位.

讓我們來建立個映射到users的model。使用WWW SQL Designer工具,我們建立了代表users表的一個表徵圖:

id欄位通常作為主鍵的形式用在所有的models裡面,每個在資料庫中的user都有一個指定的唯一id值。幸運的是,這些都是自動的,我們只需要提供一個id欄位。

nickname和email欄位被定義為string類型,他們的長度也已經被指定,這樣可以節省資料庫儲存空間。

role欄位被定義為integer類型,我們用來標識users是admins還是其他類型。


現在我們已經明確了users表的結構,接下來轉換為編碼的工作將相當簡單了(fileapp/models.py):

from app import db ROLE_USER = 0ROLE_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 '' % (self.nickname)

User類把我們剛剛建立的幾個欄位定義為類變數。欄位使用db.Column類建立執行個體,欄位的類型作為參數,另外還提供一些其他選擇性參數。例如,識別欄位唯一性和索引的參數.

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

建立資料庫

把配置和model放到正確的目錄位置,現在我們建立資料庫檔案。SQLAlchemy-migrate包內建命令列工具和APIs來建立資料庫,這樣的方式可以方便以後更新。但是我覺得使用這個命令列工具有些彆扭,所以我自己寫了個python指令碼來調用遷移的APIs.


這裡有個建立資料庫的指令碼 (filedb_create.py):

#!flask/bin/pythonfrom migrate.versioning import apifrom config import SQLALCHEMY_DATABASE_URIfrom config import SQLALCHEMY_MIGRATE_REPOfrom app import dbimport os.pathdb.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目錄下就能正常使用了。


建立資料庫你只需要運行下面的一條命令(注意windows下稍微有些不同):

./db_create.py

運行這條命令之後,你就建立了一個新的app.db檔案。這是個支援遷移的空sqlite資料庫,同時也會產生一個帶有幾個檔案的db_repository目錄,這是SQLAlchemy-migrate儲存資料庫檔案的地方,注意如果資料庫已存在它就不會再重建了。這將協助我們在丟失了現有的資料庫後,再次自動建立出來。.

第一次遷移

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

做一個遷移我使用另一個Python小助手指令碼 (filedb_migrate.py):

#!flask/bin/pythonimport impfrom migrate.versioning import apifrom app import dbfrom config import SQLALCHEMY_DATABASE_URIfrom config import SQLALCHEMY_MIGRATE_REPOmigration = 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)a = api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)print 'New migration saved as ' + migrationprint 'Current database version: ' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO))

這個指令碼看起來很複雜,其實做的東西真不多。SQLAlchemy-migrate通過對比資料庫的結構(從app.db檔案讀取)和models結構(從app/models.py檔案讀取)的方式來建立遷移任務,兩者之間的差異將作為一個遷移指令碼記錄在遷移庫中,遷移指令碼知道如何應用或者撤銷一次遷移,所以它可以方便的升級或者降級一個資料庫的格式。


雖然我使用上面的指令碼自動產生遷移時沒遇到什麼問題,但有時候真的很難決定資料庫舊格式和新格式究竟有啥改變。為了讓SQLAlchemy-migrate更容易確定資料庫的改變,我從來不給現有欄位重新命名,限制了添加刪除models、欄位,或者對現有欄位的類型修改。我總是檢查下產生的遷移指令碼是否正確。

不用多講,在你試圖遷移資料庫前必須做好備份,以防出現問題。不要在生產用的資料庫上運行第一次使用的指令碼,先在開發用的資料庫上運行下。


繼續前進,記錄下我們的遷移:

./db_migrate.py

指令碼將列印出以下資訊:

New migration saved as db_repository/versions/001_migration.py Current database version: 1

這個指令碼資訊顯示了遷移指令碼的存放位置,還有當前資料庫的版本號碼。空資料庫的版本號碼是0,當我們匯入users資訊後版本號碼變為1.

資料庫的升級和復原

現在你可能想知道為什麼我們要做額外的工作來做資料庫的遷移記錄。

試想一下,你有個應用在開發機器上,同時伺服器上也有一個複製的應用正在運行。

比方說,在你產品的下個版本你的models層作了修改,比如增加了一個新表。沒有遷移檔案的話,你需要同時解決在開發機和伺服器上資料庫格式修改的問題,這將是個很大的工作量。


如果你已經有了一個支援遷移的資料庫,那麼當你向生產伺服器發布新的應用版本時,你只需要記錄下新的遷移記錄,把遷移指令碼拷貝到你的生產伺服器上,然後運行一個簡單的應用改變指令碼就行。資料庫的升級可以使用下面的Python指令碼(filedb_upgrade.py):

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

當你運行上面的指令碼時,資料庫將升級到最新版本,並通過指令碼將改變資訊儲存到資料庫中。

把資料庫復原到舊的格式,這是不常見的一個方式,但以防萬一,SQLAlchemy-migrate也很好的支援(filedb_downgrade.py):

#!flask/bin/pythonfrom migrate.versioning import apifrom config import SQLALCHEMY_DATABASE_URIfrom config import SQLALCHEMY_MIGRATE_REPOv = 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))

這個指令碼將復原資料庫的一個版本,你可以通過運行多次的方式向前復原多個版本。

資料庫關聯

關係型資料庫最擅長儲存資料之間的關係。假如使用者會寫一篇微博,使用者的資訊被儲存在users表中,微博儲存在post表中。記錄誰寫的微博最有效方式是建立兩條資料之間的關聯.

一旦使用者和微博的關係表建立之後,我們有兩種查詢方式可以使用。.最瑣碎的一個就是當你看到一篇微博,你想知道是哪個使用者寫的。更複雜的一個是反向的查詢,如果你知道一個使用者,你想瞭解下他寫的全部微博。Flask-SQLAlchemy將給我們提供對兩種方式查詢的協助。


讓我們對資料做一下擴充來儲存微博資訊,這樣我們就能看到對應的關係了。我們回到我們使用的資料庫設計工具來建立個posts表:


posts表包含一個必須的id,微博的內容body,還有一個時間戳記。沒有什麼新東西,但是user_id欄位值得解釋下。

我們想建立使用者和他們寫的微博之間的關聯,這種方法就是通過添加一個包含使用者id的欄位來標識誰寫的微博,這個id叫做外鍵。我們的資料庫設計工具也顯示了外鍵作為一個外鍵和id欄位指向表的串連。這種關聯叫做一對多關聯,也就是一個使用者可以寫多篇文章。


讓我們修改下models來響應這些變化 (app/models.py):

from app import db ROLE_USER = 0ROLE_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 '' % (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 '' % (self.body)

我們增加了一個表示使用者寫的微博的Post類,user_id欄位在Post類中被初始化指定為一個外鍵,因此Flask-SQLAlchemy會知道這個欄位將會和使用者做關聯。

注意我們還在User類中添加了一個新欄位命名為posts,它被定義成一個db.relationship欄位,這個欄位並非是資料庫中實際存在的欄位,所以它不在我們的資料庫圖表中。對於一對多的關聯db.relationship欄位通常只需要在一邊定義。根據這個關聯我們可以擷取到使用者的微博列表。db.relationship的第一個參數表示“many”一方的類名。backref參數定義了一個欄位將"many"類的對象指回到"one"對象,就我們而言,我們可以使用psot.author擷取到User執行個體建立一個微博。如果理解不了不要擔心,在文章的後面我們將通過一個例子來解釋。


讓我們用另外一個遷移檔案記錄下這次的改變。簡單運行下面指令碼:

./db_migrate.py

運行指令碼後將得到如下輸出:

New migration saved as db_repository/versions/002_migration.py Current database version: 2

我們沒必要每次都用一個獨立的遷移檔案來記錄資料庫model層的小變化,一個遷移檔案通常只是記錄一個發布版本的改變。接下來更重要的事情是我們需要瞭解下遷移系統的工作原理。

應用實踐

我們已經花了大量的時間在資料庫定義上,但是我們仍然沒有看到他是如何工作的,因為我們的應用程式裡沒有任何的資料相關的編碼,接下來我們將在Python解譯器裡使用我們的嶄新資料庫吧。

繼續前進,啟動Python。 在 Linux 或者 OS X:

代碼如下:

flask/bin/python

Windows下:

代碼如下:

flask/Scripts/python

當你在Python命令列提示符中輸入下面資訊:

>>> from app import db, models >>>

這樣我們的資料庫模組和models就被載入到了記憶體裡.

讓我們來建立個新使用者:

>>> u = models.User(nickname='john', email='john@email.com', role=models.ROLE_USER)>>> db.session.add(u)>>> db.session.commit()>>>

在同一個會話環境下更改資料庫,多次的修改可以積累到一個會話中最後通過調用一個db.session.commit()命令提交,提交同時也保證了原子性。如果在會話中出現了錯誤,會調用db.session.rollback()把資料庫復原到會話之前的狀態。如果調用的既不是提交也不是復原,那麼系統會預設復原這個會話。Sessions(會話)保證了資料庫的資料一致性。

讓我們來添加另外一個使用者:

>>> u = models.User(nickname='susan', email='susan@email.com', role=models.ROLE_USER)>>> db.session.add(u)>>> db.session.commit()>>>

現在我們可以查詢出使用者資訊:

>>> users = models.User.query.all()>>> print users[, ]>>> for u in users:...   print u.id,u.nickname...1 john2 susan>>>

此處我們使用了query查詢函數,在所有的model類中都可以使用這個函數。注意id是如何自動產生的。

還有另外一種方式來查詢,如果我們知道了使用者的id,我們可以使用下面的方式尋找使用者資訊:

>>> u = models.User.query.get(1)>>> print u>>>

現在讓我們添加一條微博資訊:

>>> import datetime>>> u = models.User.query.get(1)>>> p = models.Post(body='my first post!', timestamp=datetime.datetime.utcnow(), author=u)>>> db.session.add(p)>>> db.session.commit()


這個地方我們把時間設定為UTC時區,所有的儲存在資料庫裡的時間將是UTC格式,使用者可能在世界各地寫微博,因此我們需要使用統一的時間單位。在以後的教程中我們將學習如何在使用者本地時區使用這些時間。

你也許注意到我們沒有在Post類中設定user_id欄位,取而代之的是把使用者物件儲存到了author欄位。auhtor欄位是個通過Flask-SQLAlchemy添加的虛擬欄位用來建立關聯關係的,我們之前已經定義好了這個名字,參照:model中的db.relationship中backref參數。通過這些資訊,ORM層就能知道如何取到user_id。


要完成這個會話,讓我們來看看更多可做的資料庫查詢:

# get all posts from a user>>> u = models.User.query.get(1)>>> print u>>> posts = u.posts.all()>>> print posts[] # obtain author of each post>>> for p in posts:...   print p.id,p.author.nickname,p.body...1 john my first post! # a user that has no posts>>> u = models.User.query.get(2)>>> print u>>> print u.posts.all()[] # get all users in reverse alphabetical order>>> print models.User.query.order_by('nickname desc').all()[, ]>>>

要瞭解更多的資料庫查詢選項,最好的方式就是去看 Flask-SQLAlchemy 的文檔。

在結束會話之前,我們把之前建立的測試使用者和文章刪除掉,就可以在接下來的章節,從一個乾淨的資料庫開始:

>>> users = models.User.query.all()>>> for u in users:...   db.session.delete(u)...>>> posts = models.Post.query.all()>>> for p in posts:...   db.session.delete(p)...>>> db.session.commit()>>>


結束語

這一長篇新手入門,我們瞭解到了資料庫的基本操作,但我們還沒有將資料庫關聯到程式中。在下一個章節中我們通過使用者登入系統來練習所學的資料庫操作。

在此同時,如果你還沒開始寫程式,你需要下載當前檔案 microblog-0.4.zip.注意,zip檔案中沒有包括資料庫,但是已經有儲存指令碼。用db_create.py建立一個新的資料庫,用db_upgrade.py把你的資料庫升級到最新版本。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.