2011年11月23日 星期三

使用 Django 的雜感

換工作後大概很少會用到 Django, 在這對它做個了結吧。

先說結論

對於不熟 web 又想用 python 開發的人來說, Django 仍是首選, 主要的優勢有:

  1. 官方文件超級豐富, 網路上文件也豐富, 也有出一些書, 不過書上內容應該沒官網新, 看官網就夠了。不夠的話再看原始碼也比較方便。
  2. 社群龐大, 有許多 middleware 和 plugin 可用, 像多人合寫 web 一定會需要 database integration, South 相當好用。其它像用 Facebook / Google / Yahoo 等帳號登入, 也都有整合好的套件可用。
  3. 框架本身實作了許多 web 相關功能 (像 session、cookie、cache、傳輸 zip 後的內容), 可避免犯錯 (像是有擋 CSRF), 很多東西是新手從來沒想過的, 但框架都有考慮到並且實作好了。在使用的過程中可學到這些知識。
  4. 極佳的向下相容。官方的 roadmap 會明確提到那些功能已 deprecated, 並會在未來的那一版移掉, 有滿長的過渡期。使用者升級的負擔相當小。
  5. 提供一組規範, 切開 model、view、control, 還有 class / table 命名規則等, 減少團隊合作的問題。

當初選 Django 的主因是第一、二點, 第三點稍微有想到, 而第四、五點是使用後得到的驚喜。

說完結論後, 要開始碎碎念我的不滿, 請各位看倌記得, 即使如此, 我還是推薦不熟 web 又想用 python 開發的人使用 Django, 理由如前所述。

關於 template

Django 內建的 template 不好用, 效率也差, 甚至在 FAQ 裡有一項提到「I can't stand your template language. Do I have to use it?」。有些人可能覺得 template 的效率不是重點, 瓶頸會在 database。當我費盡心力減少 SQL、改 schema、改index、改寫 SQL 將 database 花的時間壓到極致後, 卻發現 template 怎麼縮都要 0.1s, 讓我很無力。更別提在關掉 i18n / l10n 前, template 要 0.2s。看著簡單的 template 內容, 很難理解為什麼這樣的東西要花到 0.1s。

此外, template 的語法很受限, 不過到 1.3 版後 include 多了 with 的語法方便代入子版面後, 變得好用許多, 不然有類似這種簡單需求時要另寫 templatetag, 實在是多餘又不易懂。相關心得見之前寫的文章。實在是換掉也為難, 不換也為難。

關於 ORM

先來個免責聲明, 若是需要頻繁地寫多個不同的小型網站, 用 ORM 是利多於弊, 可減少重覆的程式碼。以下的論點基於我的個人經驗, 需求是長期維護一個資料量大且有嚴苛速度需求的網站。

之前已寫過幾篇 ORM 心得, 在《Django 和 Python 操作 database 時的額外負擔》提到實測大量數據的情況有多慢, 注意我的使用情境有超過百萬筆資料, 若資料量沒那麼大, 這個負擔較無所謂。

《撰寫資料庫相關程式的心得》有提到使用 ORM 的成本。這裡要補充的是, 無論如何, 我們都需要分開「資料庫的操作」和「邏輯操作」。而在程式裡直接使用 ORM 並沒有隔離好兩者。

舉例來說, 在頁面裡使用 get_user("fcamel") 會比 User.objects.get(name="fcamel") 來得好。想想要如何針對這個頁面寫 unit test, 會發覺後者仍和 database 有很大的相依性, 不容易 mock。或著換個說法, get_user() 的抽象程度比直接使用 ORM 高。get_user() 裡是使用 ORM 還是下 raw SQL, 都和使用者無關。當需要連續幾個 ORM 操作以達成一個目的時, 另外包函式的優勢會更明顯。

若能接受上面的論點的話, 會發覺 ORM 的優勢又少了一點。所以, 我個人的看法是: 重點是必須另外包一層 API 存取資料, 供邏輯操作使用。所以, 對前端開發者來說, 是否使用 ORM, 影響不大。但對後端開發者來說, ORM 的缺點遠大於優點。ORM 最吸引人的地方是提供不錯介面隔離 database, 可以有彈性地存取資料並且不用太了解如何寫 SQL。但隨著使用經驗漸增, 會發覺這些優點並非事實。但若一直不去了解 database, 不會發覺付出的隱性成本。

題外話, 聽說 SQLAlchemy 相當強大, 不知實際用起來效果如何。我後來淡化使用 ORM 的場合, 加上需要使用 South (基於 Django ORM 的 plugin), 不方便換掉, 就沒有研究 SQLAlchemy 了。

關於 test

內建的 django.test 相關模組不太好用, 而且執行 test 要先重建全部 table, 然後在每個 test case 前 truncate 全部 table, 效率不好。若能指定只 truncate 需要的 table, 可省下許多時間。通常 test case 是愈寫愈多, 實務上執行 test case 的效率相當重要。

使用 South 後更會有 production schema 和 test schema 不同的問題, 因為 production 用 South 建 schema, 中間可能用到客制化的 SQL 改 schema (如建 multiple column indexes), 但 django.test 不會呼叫 South, 而是用 Django 原本讀 models.py 建 schema 的方式。我後來改用自己寫的模組來建 test database, 沒研究後續發展, 不知後來是否有修正。

關於 coding style

Django 違反許多 PEP 8 的規則或 Python 精神, 像是:

  • 在程式碼裡面 import 別的 module (而非開頭), 並且有 circular import。
  • 有多種方法做一件事。我很討厭這點, 像自訂 login 的重導頁面卻沒成功, 除錯時很麻煩, 要搞清楚多種規則的執行順序, 才知道問題出在那。
  • 有許多 lazy initialization。我不確定這是否違反 Python 社群的 "explicit is better than implicit", 我個人不喜歡一堆 lazy initialization, 很難掌握程式的行為。

以上這些事對使用者有什麼影響? 當行為不合預期, 文件也看不出所以然時, 讀原始碼並加 log message 是滿有效率的除錯方法。上述都是我在研究功能 (像是如何使用 cache) 或除錯時, 讀原始碼遇到的困擾。

結語

除前面一再強調的「結論」外, 再多強調一下, 一個東西愈多人罵, 表示愈多人用, 出事也愈好處理。本篇不是建議別用 Django, 也不是反串推廣, 只是之前一年多的使用心得。