こんにちは! 私が大好きなことの1つは、20年以上前から存在しているのに今まで触れたことのない、昔からある退屈な技術を学び始めることです。直面するであろうすべての問題がすでに何千回も解決されているので、簡単に作業を進められるのが本当に気持ちいいです。

私は長い間、RailsやDjango、Laravelのような人気のWebフレームワークを学んでみたいと思っていましたが、なかなか実現できませんでした。でも数ヶ月前にWebサイトを作るためにDjangoを学び始め、これまで気に入っています。ここにいくつかの簡単なメモを残します!

Railsよりマジックが少ない

2020年にRailsを学ぼうとしたことがあり、Railsは素晴らしく、Rubyコミュニティも素晴らしいので本当に気に入りたかったのですが、数ヶ月Railsプロジェクトを放置した後で戻ってきたときに、何かを進める方法を思い出すのが難しいことに気づきました。たとえば、routes.rbresources :topicsと書かれているだけでは、topicsのルートがどこで設定されているかがわからず、慣習を思い出すか調べる必要があるのです。

数ヶ月または数年プロジェクトを放置してから戻ってくることが私にとっては本当に重要です(私のすべてのプロジェクトがそうなっています!)。Djangoの方が明示的で、私には簡単だと感じます。

私の小さなDjangoプロジェクトでは、主なファイルは(設定ファイルを除いて)urls.pymodels.pyviews.pyadmin.pytests.pyの5つだけで、何か他の場所(HTMLテンプレートなど)を知りたい場合は、通常これらのファイルのいずれかから明示的に参照されています。

組み込みの管理画面

このプロジェクトでは、データベース内のデータを手動で編集または閲覧するための管理インターフェースが欲しかったのです。Djangoには本当に優れた組み込みの管理インターフェースがあり、少しのコードでカスタマイズできます。

たとえば、私の管理クラスの一部を紹介します。これは「リスト」ビューに表示するフィールド、検索対象のフィールド、デフォルトの並び順を設定しています。

@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)

このクエリはzineszine_productsproductsorder_productsordersの5つのテーブルに関わります。これを実現するには、「orders」と「products」を関連付けるManyToManyFieldと、「zines」と「products」を関連付けるもう1つのManyToManyFieldをDjangoに伝えるだけで、zinesordersproductsをどう接続するかをDjangoが把握してくれます。

確かに自分でそのクエリを書くことはできますが、product__order__email_hashと書く方が入力が少なく、読みやすく、正直に言うとクエリを構築する方法を考えるのに少し時間がかかると思います(このクエリには結合以外にもいくつか処理が必要なのです)。

ORMが生成するクエリのパフォーマンスについては全く心配していないので、今のところORMにワクワクしていますが、いつかフラストレーションを感じる点が出てくるでしょう。

自動マイグレーション!

ORMのもう1つの素晴らしい点はマイグレーションです!

models.pyでフィールドを追加・削除・変更すると、Djangoは自動的にmigrations/0006_delete_imageblob.pyのようなマイグレーションスクリプトを生成します。

必要ならこれらのスクリプトを編集できると思いますが、今のところ生成されたスクリプトをそのまま実行していて問題なく動作しています。本当に魔法のようです。

マイグレーションを簡単に実行できることは、私にとって今とても重要だと実感しています。なぜなら、データモデルの望ましい形を模索する中で、かなり頻繁にデータモデルを変更しているからです。

ドキュメントが好き

私はドキュメントを読まないという悪い癖がありましたが、Djangoのドキュメントを読んだ部分はとても気に入っています。これは偶然ではなく、Jacob Kaplan-MossがPyCon 2011でDjangoのドキュメント文化について講演しています。

たとえば、モデルの概要では、ORMを使う際に設定したい最も重要な一般的なフィールドが一覧になっています。

SQLiteを使う

Postgresを運用しようとして何が起きているのか理解できず嫌な経験をした後、すべての小さなWebサイトをSQLiteで運用することにしました。ずっと調子が良く、VACUUM INTOを実行して生成された単一のファイルをコピーするだけでバックアップできるのが気に入っています。

本番環境でDjangoとSQLiteを使うためのこれらの手順に従っています。

サイトの書き込みは1日あたり最大数百件程度と予想しているので問題ないはずです。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')

これで、他の基本的なWebサイト機能もDjangoにすでに組み込まれていて簡単に実現できそうだと思えました。

設定ファイルはまだ多いと感じる

settings.pyファイルにはまだ少し圧倒されています。Djangoの設定システムはファイル内で多くのグローバル変数を設定する方式で、変数名のタイポをしてしまったらどうしよう…と少しストレスを感じます。たとえばWSGI_APPLICATIONの代わりにWSGI_APPLICATOIN = "config.wsgi.application"と入力してしまったらどうなるでしょうか?

Python言語サーバーがタイポを教えてくれることに慣れてしまったので、言語サーバーのサポートに頼れないのは少し混乱します。

今回はこれで!

私はこれまで実際のWebフレームワークをプロジェクトでうまく使ったことがありません(現在、私のWebサイトのほとんどは単一のGoバイナリか静的サイトです)。なので、これからどうなるか見てみたいと思います!

まだ学ぶべきことはたくさんあります。Djangoのフォームバリデーションツールや認証システムについてはまだほとんど触れていません。

ORMにチャンスを与えてくれたMarco Rogersに感謝します。

(Mastodonでのコメントシステムをまだ実験中です! Mastodonでのコメントはこちら! あなたのお気に入りのDjango機能を教えてください!)