Some notes on starting to use Django

Julia Evans

開始使用 Django 的一些筆記

原文由 Julia Evans 發布,訂閱此部落格

哈囉!我最喜歡做的事情之一,就是開始學習一種我從沒試過、卻已經存在 20 多年的「老派無聊技術」。當我遇到的每個問題都早已被人解決過上千次,讓我可以輕鬆把事情搞定時,感覺真的很棒。

我一直覺得學一套熱門的網頁框架,像 Rails、Django 或 Laravel 應該會很酷,但始終沒能真正付諸實行。幾個月前,為了做一個網站,我開始學 Django,到目前為止還滿喜歡的,以下是一些快速筆記!

比 Rails 少一點魔法

我在 2020 年花了一些時間嘗試學習 Rails,雖然它很酷、我也很想喜歡上 Rails(Ruby 社群真的很棒!),但我發現如果把 Rails 專案擱置幾個月後再回來,就很難記得要怎麼繼續做事。舉例來說,如果在 routes.rb 裡寫了 resources :topics,光看這一行根本看不出 topics 的路由是在哪裡設定的,你得記得或是去查它的慣例才行。

能夠把一個專案丟著幾個月甚至幾年後再回來繼續做,對我來說非常重要(我所有的專案都是這樣運作的!),而 Django 對我來說感覺更簡單,因為它的寫法更明確。

在我的小型 Django 專案裡,感覺就只有 5 個主要檔案(設定檔不算):urls.pymodels.pyviews.pyadmin.pytests.py,如果我想知道其他東西(比如 HTML 樣板)在哪裡,通常都能在這些檔案中找到明確的參照。

內建的管理後台

在這個專案中,我想要有一個管理介面來手動編輯或檢視資料庫裡的一些資料。Django 有一個非常好用的內建管理介面,而且我只要寫一點點程式碼就能客製化它。

例如,這是我的其中一個 admin 類別的一部分,它設定了在「列表」檢視中要顯示哪些欄位、要針對哪個欄位搜尋,以及預設的排序方式。

@admin.register(Zine)
class ZineAdmin(admin.ModelAdmin):
    list_display = ["name", "publication_date", "free", "slug", "image_preview"]
    search_fields = ["name", "slug"]
    readonly_fields = ["image_preview"]
    ordering = ["-publication_date"]

有 ORM 還滿好玩的

以前我的態度是「ORM?誰需要那種東西?我自己寫 SQL 查詢就好了!」。不過到目前為止我還滿享受 Django 的 ORM 的,我覺得 Django 用 __ 來表示 JOIN 的設計很酷,像這樣:

Zine.objects
    .exclude(product__order__email_hash=email_hash)

這個查詢牽涉到 5 張資料表:zineszine_productsproductsorder_productsorders。要讓它運作,我只需要在 Django 裡告訴它有一個 ManyToManyField 關聯了「orders」和「products」,還有另一個 ManyToManyField 關聯了「zines」和「products」,這樣它就知道該怎麼把 zinesordersproducts 連起來。

我當然可以自己寫出那個查詢,但寫 product__order__email_hash 要打的字少很多,讀起來也容易得多,老實說,要我自己拼出那個查詢(除了那些 JOIN 之外還需要做其他一些事),恐怕還得花上一點時間。

我目前完全不擔心 ORM 產生的查詢的效能,所以現在對 ORM 還滿興奮的,不過我確定遲早還是會遇到讓人感到挫折的地方。

自動化的遷移!

ORM 另一個很棒的地方就是遷移!

如果我在 models.py 裡新增、刪除或修改一個欄位,Django 就會自動產生像 migrations/0006_delete_imageblob.py 這樣的遷移指令稿。

我想如果需要的話,我當然可以去編輯那些指令稿,但到目前為止我都是直接執行自動產生的指令稿,完全不需修改,運作得非常順利。真的有種魔法的感覺。

我體認到能夠輕鬆地做遷移對現在的我來說很重要,因為我在摸索資料模型該怎麼運作的過程中,還滿頻繁地在修改它。

我喜歡它的文件

我以前有個壞習慣是從來不看文件,但到目前為止,我讀過的 Django 文件都讓我非常喜歡。這不是偶然:Jacob Kaplan-Moss 在 PyCon 2011 的一場演講中就談過 Django 的文件文化。

例如,模型的介紹就列出了使用 ORM 時你可能會想設定的幾個最重要、常見的欄位。

使用 SQLite

在嘗試維運 Postgres 卻搞不清楚到底發生什麼事的糟糕經驗之後,我決定把我所有的小型網站都改用 SQLite 來跑。結果好得多,而且我很喜歡只要執行 VACUUM INTO 再把產生的單一檔案複製起來就能備份的方式。

我一直是照著這份說明在正式環境中搭配 Django 使用 SQLite。

我想應該不會有問題,因為我預期這個網站一天最多只有幾百次寫入,遠比Mess with DNS 少很多,而後者有更多的寫入量也一直運作得很順利(雖然它的寫入分散在 3 個不同的 SQLite 資料庫中)。

內建的電子郵件功能(還有更多)

Django 感覺非常「電池已含」(batteries-included),這點我很喜歡——不管是想要 CSRF 防護、Content-Security-Policy,還是想要寄送電子郵件,裡面全都有!

舉例來說,我想在開發模式下把 Django 寄出的電子郵件存成檔案(這樣就不會真的寄信給真實的使用者),這只需要一點點設定。

我只需要在 settings/dev.py 裡加上這段:

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = BASE_DIR / "emails"

然後在 settings/production.py 裡這樣設定正式環境的郵件:

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.whatever.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "xxxx"
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_API_KEY')

這讓我覺得,如果我想要其他基本的網站功能,Django 裡面很可能已經內建了簡單的作法。

設定檔還是讓人有點吃不消

我對 settings.py 這個檔案還是有點敬畏:Django 的設定系統是在一個檔案裡設定一堆全域變數,讓我有點焦慮……如果我把其中某個變數名稱打錯了怎麼辦?我要怎麼知道?如果我把 WSGI_APPLICATION 打成 WSGI_APPLICATOIN = "config.wsgi.application" 呢?

我想是因為我已經習慣有 Python 語言伺服器在打錯字時提醒我,所以現在無法依賴語言伺服器的支援時,就感覺有點無所適從。

就先這樣!

我以前還沒有真正成功地在專案中使用過網頁框架(目前我幾乎所有的網站不是單一的 Go 執行檔,就是靜態網站),所以我很期待看看接下來的發展!

還有很多東西等著我去學,我還沒真正接觸過 Django 的表單驗證工具或認證系統。

感謝 Marco Rogers 說服我給 ORM 一個機會。

(我們還在試驗 Mastodon 上的留言系統!在 Mastodon 上查看留言!告訴我你最喜歡的 Django 功能是什麼!)

本文章由 muse-spark-1.2-contributor 進行翻譯

留言