Flask實踐:待辦事項(ToDo-List)

Demo:task5.herokuapp.com/

源碼:github.com/helloflask/t

這一次我們使用Flask來實現一個待辦事項(To-Do List)應用。這篇文章主要介紹這個應用的大致實現過程和一些讓交互更簡潔的處理技巧。在續篇里,我會介紹使用jQuery(AJAX)把它改造成一個單頁應用(SPA,Single-page application)。

因為大家對Bootstrap已經很熟悉了,所以這裡用了上篇文章里提到的Materialize框架。

簡化模板

不論是從用戶體驗的角度,還是從減少工作量,提高載入速度等方面考慮,頁面和跳轉應該儘可能的少一些,尤其是這樣的工具類應用。這可以通過優化模板實現,也可以通過JavaScript實現。

舉兩個例子。

一、編輯條目

我找到了另一個使用Flask實現的To-Do List應用,做個參照。在這個應用里,加上基模板,她一共用了4個頁面。

Demo:flask-todoapp.herokuapp.com

編輯一個條目的流程是這樣的:

  1. 點擊編輯按鈕,頁面跳轉到另一個模板
  2. 渲染出表單,編輯內容
  3. 提交後跳轉回主頁面。

當你點擊新建或是編輯條目/分類時,會跳轉到另一個頁面:

這其實只要一個頁面就夠了:

藉助jQuery,我們可以這樣實現:

  1. 渲染主頁面的條目時,為每一個條目附加一個編輯表單,使用CSS隱藏它(display: none)
  2. 點擊編輯按鈕時,隱藏條目,顯示錶單(使用jQuery的hide和show函數)
  3. 表單提交後跳轉到原頁面

這樣一來,就只需要一個頁面了。在續篇里,使用AJAX技術在後台傳送數據,體驗會更好。

二、刪除條目

想像一下,你在豆瓣收藏了很多喜歡的條目,你想清理一下,在頁面最底端你刪除了一個條目,如果這時頁面在刪除後跳轉,就回到頁面頂端了,可你還想刪除在底端的另一些條目。因為刪除不用傳遞數據,我們可以用這個小技巧解決:

在頁面上,點擊刪除按鈕後,使用jQuery的slideUp()函數隱藏條目,在後台仍然指定相應的視圖函數,但是這個函數不跳轉到任何頁面(只是在資料庫里刪除條目),只返回下面的204狀態碼(代表無內容)。

return (), 204n

這樣刪除條目的體驗會更加流暢和自然。

資料庫框架SQLAlchemy

因為程序很小,我這次仍然把配置選項放在單個文件里。這次使用了資料庫框架SQLAlchemy。使用Flask擴展Flask-SQLAlchemy簡化操作,資料庫使用SQLite。和其他擴展一樣,安裝後初始化並配置資料庫:

from flask_sqlalchemy import SQLAlchemynnbasedir = os.path.abspath(os.path.dirname(__file__))napp = Flask(__name__)napp.config[SECRET_KEY] = a secret stringnapp.config[SQLALCHEMY_DATABASE_URI] = n sqlite:/// + os.path.join(basedir, data.sqlite)napp.config[SQLALCHEMY_COMMIT_ON_TEARDOWN] = Truenapp.config[SQLALCHEMY_TRACK_MODIFICATIONS] = Truenndb = SQLAlchemy(app)n

解釋一下這裡的配置選項:

  • SQLALCHEMY_DATABASE_URI:這裡要注意拼寫,不要把URI寫成URL。前一個是通用資源表示號(Universal Resource Identifier),後者是統一資源定位符(Uniform Resource Locator)。
    • 在《Flask Web開發》里,介紹資料庫時用的是URL,導致很多人在後面的配置里也寫成了URL。簡單來說,URL是URI的子集。更詳細的解釋見:stackoverflow.com/quest
    • 注意在Linux主機部署時,資料庫URI里是四根斜線。

  • SQLALCHEMY_COMMIT_ON_TEARDOWN:按字面理解就是,在每次請求結束後自動提交資料庫會話,也就是db.session.commit()。
    • 在2.0版本里,這個配置選項被認為有害,從文檔里刪掉了,在這個issue里討論了這個問題。如果不想手動提交資料庫會話,你可以繼續使用它。
    • 但是有時候,你需要手動提交,比如在這個應用里,有一個創建新的分類的函數,創建後使用redirect函數跳轉到這個新建的分類(需要分類的id作為參數),如果不提交資料庫會話,就沒法獲得新建分類的id。

@app.route(/new-category, methods=[GET, POST])ndef new_category():n name = request.form.get(name)n category = Category(name=name)n db.session.add(category)n db.session.commit() # commit後使下面的category.id獲得實際值nreturn redirect(url_for(category, id=category.id))n

  • SQLALCHEMY_TRACK_MODIFICATIONS:啟動項目後會出現一個警告,我不太清楚這個配置的作用(跟蹤對象的修改?)。將它設為True可以抑制這個警告。

上述配置會在你程序根目錄下生成一個data.sqlite文件。而最後實例SQLAlchemy類的db對象表示程序的資料庫,你在視圖函數里使用它(db)來操作資料庫。

一對多關係

在這個應用里,用到了一對多關係(條目和分類)。這是條目和分類的模型(Model):

class Item(db.Model):n __tablename__ = itemsn id = db.Column(db.Integer, primary_key=True)n body = db.Column(db.Text)n category_id = db.Column(db.Integer, db.ForeignKey(categories.id)) # step2nnnclass Category(db.Model):n __tablename__ = categoriesn id = db.Column(db.Integer, primary_key=True)n name = db.Column(db.String(64))n items = db.relationship(Item, backref=category) # step1n

實現一個一對多關係需要兩步:

  1. 在Category模型里relationship函數定義一個引向子模型的對象(items)
  2. 在Item模型里定義一個外鍵(category_id),並添加一個到Category模型的反向引用(backref)

在存儲一個條目到資料庫時,同時也要指定一個分類,這裡的分類需要是一個對象(Object),而不是id:

body = request.form.get(item) # 條目的內容nid = request.form.get(category) # 分類的idncategory = Category.query.get_or_404(id) # 分類的對象nitem = Item(body=body, category=category)ndb.session.add(item)n

這次還用到了另一個擴展——Flask-Script,用來增強命令行功能。暫時沒有太深入的應用,有機會再作總結。

具體實現

程序比較簡單,具體實現沒太多需要介紹的。和之前不同的是,這次我使用jQuery對錶單進行簡單的驗證。感興趣的話,可以去體驗一下demo,或是看看源碼。為了方便體驗(偷懶),沒有添加用戶系統,你可以隨便玩,但條目和分類的數量做了一些限制。

目前功能比較簡單,後續會加上拖拽排序,設置日期,設置優先順序等功能(考慮加上像wunderlist那樣的音效……)。

安裝和下載見介紹頁:helloflask.github.io/to

續篇見!

相關鏈接

  • Demo:task5.herokuapp.com/

  • 介紹頁:helloflask.github.io/to

  • 源碼:github.com/helloflask/t

  • docs.sqlalchemy.org/en/

  • github.com/mitsuhiko/fl

  • flask-sqlalchemy.pocoo.org

- - - - -

更多關於Flask和Web開發的優質原創內容,歡迎關注Hello, Flask! - 知乎專欄。


推薦閱讀:

flask框架中應用上下文跟請求上下文是什麼意思?
有一點 Python 與 SQL 基礎,想學習開發網站,Django,Karrigell,Webpy 這 3 個哪個更加適合我呢?
第一期 · 如何理解Flask中的藍圖?
Flask項目配置(Configuration)
《Flask Web Development》問題集

TAG:Flask | Web开发 | 数据库 |