Zi 字媒體
2017-07-25T20:27:27+00:00
熟悉 Java 的朋友們一定使用過 Hibernate 或 MyBatis 吧,這類的框架稱為對象關係映射 ORM 框架,它將對資料庫的操作從繁瑣的 SQL 語言執行簡化為對象的操作。Python 中也有類似的 ORM 框架,叫 SQLAlchemy。本篇我們將介紹 Flask 中支持 SQLAlchemy 框架的第三方擴展,Flask-SQLAlchemy。安裝和啟用在閱讀此文之前,強烈建議讀者先了解 SQLAlchemy 的基本知識。我們依然通過 pip 安裝:$ pip install Flask-SQLAlchemyPyPI 自動會將其所依賴的 SQLAlchemy 包裝上。我們可以採用下面的方法初始化一個 Flask-SQLAlchemy 的實例:from flask importFlaskfrom flask.ext.sqlalchemy importSQLAlchemy app =Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///db/users.db'db =SQLAlchemy(app)應用配置項"SQLALCHEMYDATABASEURI"指定了 SQLAlchemy 所要操作的資料庫的連接字元串,本文中我們使用 SQLite3,連接字元串以"sqlite:///"開頭,後面的"db/users.db"表示資料庫文件是當前位置下 db 子目錄中的"users.db"文件。定義模型一個模型即對應資料庫中的一個表,這裡我們來定義一個用戶模型:classUser(db.Model): id = db.Column(db.Integer primary_key=True) name = db.Column(db.String(50), unique=True) age = db.Column(db.Integer) def __init__(self name age): self.name = name self.age = age def __repr__(self): return''%self.name模型類必須繼承"db.Model", db 即上一節的"db = SQLAlchemy(app)",上例中的 User 模型將自動映射到資料庫中的"user"表。User 模型中定義了三個屬性:"id":整型主鍵"name":最大長度為 50 的字元串,且值唯一"age":整型這三個屬性將分別對應"user"表中"id"主鍵, "name"和"age"欄位。寫好"init"和"repr" 方法,我們的模型就定義完成了。現在你就可以通過下面的代碼來創建資料庫和表: db.create_all讓我們來驗證下,"user"表是否創建成功。首先打開資料庫文件:$ sqlite3 db/users.db查詢下"user"表的 schema:sqlite.schema user你應該可以看到下面的信息:CREATE TABLE user (id INTEGER NOT NULLname VARCHAR(50),age INTEGERPRIMARY KEY (id),UNIQUE (name));另外,你可以通過"db.drop_all"方法刪除所有的表,不過資料庫文件將會被保留。添加數據數據表創建完后,讓我們添加些數據進去: db.session.add(User('Michael'18)) db.session.add(User('Tom'21)) db.session.add(User('Jane'17)) db.session.commit一定要記得調用"db.session.commit"提交事務,不然數據不會保存到資料庫中。我們無需指定每條記錄的"id"主鍵值,資料庫會自動使用自增的數值作為主鍵。查詢數據每個數據模型都有"query"介面可以用來查詢模型所對應的表的記錄。比如,查詢"user"表中的所有記錄: users =User.query.all返回的 users 是一個列表,其中每個元素都是一個 User 類型的對象,對應於"user"表中的一條記錄。該方法相當於執行了 SQL 語句:SELECT * FROM user"query"介面擁有豐富的方法,這裡列舉一些常用的:"filter_by"方法,對查詢結果過濾,參數必須是鍵值對"key=value" # WHERE name='Tom' users =User.query.filter_by(name='Tom') # WHERE name='Tom' AND age=17 users =User.query.filter_by(name='Jane' age=17)效果相當於使用了 WHERE 子句,多個鍵值對用逗號分割。"filter"方法,對查詢結果過濾,比"filter_by"方法更強大,參數是布爾表達式 # WHERE age<20 users =User.query.filter(User.age<20) # WHERE name LIKE 'J%' AND age<20 users =User.query.filter(User.name.startswith('J'),User.age<20)多個查詢條件用逗號分割。"first"方法,取返回列表中的第一個元素,當我們只查詢一條記錄時非常有用 user =User.query.filter_by(name='Michael').first"order_by"方法,排序 from sqlalchemy import desc # ORDER BY name user =User.query.order_by(User.name) # ORDER BY age DESC, name user =User.query.order_by(desc(User.age),User.name)"limit"和"offset"方法,分頁 # LIMIT 10 OFFSET 10 user =User.query.limit(10).offset(10)等同於 MySQL 中的 LIMIT 和 OFFSET,上例中我們從第 11 條記錄開始取,並最多只取 10 條。"slice(start, stop)",分頁 # LIMIT 2 OFFSET 1 user =User.query.slice(13)從 start 位置開始取記錄,到 stop 位置前結束。本質上來說,SQLAlchemy 會將其翻譯成 LIMIT/OFFSET 語句來實現,上例中的"slice(1, 3)"等同於"LIMIT 2 OFFSET 1″。更新數據在添加數據時,我們使用了"add"方法,其實它一樣可以用來更新數據: user =User.query.filter_by(name='Tom').first if user isnotNone: user.age =1 db.session.add(user) db.session.commitSQLAlchemy 會自動判斷,如果對象對應的記錄已存在,就更新而不是添加。SQLAlchemy 還支持批量更新,比如我們要將所有歲數小於 20 的人都加 1 歲: User.query.filter(User.age<20).update({'age':User.age 1}) db.session.commit更新完后,別忘了提交事務。刪除數據只需調用"delete"方法即可,傳入的參數是對應資料庫中記錄的對象。記得同"add"一樣,要調用"commit"來提交事務: user =User.query.filter_by(name='Michael').first if user isnotNone: db.session.delete(user) db.session.commit一對多關係現在讓我們再添加一個模型,成績單。每個用戶對於不同的課程,會有不同的分數,這樣用戶同成績單之前就是一對多的關係。怎麼在模型類的定義中體現這個一對多關係呢。保持 User 類不變,現在讓我們添加一個 Score 類:from datetime import datetime classScore(db.Model): id = db.Column(db.Integer primary_key=True) course = db.Column(db.String(50)) assess_date = db.Column(db.DateTime) score = db.Column(db.Float) is_pass = db.Column(db.Boolean) user_id = db.Column(db.Integer db.ForeignKey('user.id')) user = db.relationship('User' backref=db.backref('scores' lazy='dynamic')) def __init__(self course score user assess_date=None): self.course = course self.score = score self.is_pass =(score >=60) if assess_date isNone: assess_date = datetime.now self.assess_date = assess_date self.user = user def __repr__(self): return''%(self.courseself.user.name)Score 模型中有這些屬性:"id":整型主鍵"course":最大長度為 50 的字元串"assess_date":日期時間類型"score":浮點型"is_pass":布爾型分別對應資料庫"score"表中"id"主鍵, "course", "accessdate", "score"和"ispass"欄位。另外,它還有兩個屬性:"user_id":整型外鍵,對應於"user"表的主鍵"id""user":User 對象"user_id"欄位聲明了外鍵,也就相當於聲明了"user"表同"score"表的一對多關係。"user"屬性並不是數據表中的欄位,它使用了"db.relationship"方法,使得我們可以通過"Score.user"訪問當前 score 記錄的 user 對象,它的第一個參數"User"就表明了對應的對象模型是 User。而第二個參數"backref"定義了從 User 模型反向引用 Score 模型的方法,上例中,我們就可以用"User.scores"獲取當前 user 對象所有的 score 記錄,它是一個列表。"db.backref"方法的"lazy"參數決定了在 User 對象中什麼時候載入其 scores 列表的值,延遲載入可以提高性能,並避免內存的浪費,"lazy"參數的選擇可以 參閱這裡。現在查詢下"score"表的 schema,你會看到下面的結果:CREATE TABLE score (id INTEGER NOT NULLcourse VARCHAR(50),assess_date DATETIMEscore FLOATis_pass BOOLEANuser_id INTEGERPRIMARY KEY (id),CHECK (is_pass IN (01)),FOREIGN KEY(user_id) REFERENCES user (id));讓我們添加些 score 記錄: user =User.query.filter_by(name='Tom').first if user isnotNone: db.session.add(Score('Math'80.5 user)) db.session.add(Score('Politics'58 user)) user =User.query.filter_by(name='Jane').first if user isnotNone: db.session.add(Score('Math'88 user)) db.session.commit然後試試通過"User.scores"查詢某個用戶的成績:def scores(name): user =User.query.filter_by(name=name).first if user isnotNone: for score in user.scores: print'Name "%s" course "%s", score is %s'%(name score.course score.score)對於多對多關係,大家可以創建一個單獨的關係表,然後每個表同這個關係表都是一對多的關係。或者大家可以參考 官方文檔上的例子來實現多對多關係。更多參考資料SQLAlchemy 的官方文檔Flask-SQLAlchemy 的官方文檔Flask-SQLAlchemy 的源碼題圖:pexels,CC0 授權。
寫了
5860316篇文章,獲得
23313次喜歡