Some notes on starting to use Django

Julia Evans

Djangoを使い始めてのちょっとしたメモ

原文は Julia Evans により に公開されました。 このブログを購読する

こんにちは!私の大好きなことの一つは、今まで触ったことはないけれど20年以上前から存在しているような「枯れた退屈な技術」を学び始めることです。自分がこれから遭遇するであろう問題が、すでに1000回も解決されていて、あとはサクサク作業を進められるだけ、という状況は本当に気持ちがいいものです。

RailsやDjango、Laravelのような人気のWebフレームワークを学んでみたいとずっと思っていたのですが、なかなか実現できずにいました。でも数ヶ月前、あるウェブサイトを作るためにDjangoを学び始め、今のところとても気に入っています。そこで、ちょっとしたメモをいくつか残しておきます!

Railsより魔法が少ない

2020年に少しRailsを学んでみたことがありますが、クールだし本当はRailsを好きになりたかった(Rubyコミュニティは素晴らしいです!)のに、数ヶ月プロジェクトを放置してから戻ってくると、何をどうすればいいのか思い出すのが難しいと感じました。たとえば、routes.rbresources :topicsと書いてあっても、それだけではtopicsのルーティングがどこで設定されているのか分からず、規約を思い出すか調べ直す必要があるからです。

プロジェクトを数ヶ月、あるいは数年放置してからまた戻ってこられることは、私にとってすごく重要です(私のプロジェクトは全部そういうやり方なんです!)。Djangoの方が物事がより明示的なので、私には扱いやすく感じます。

私の小さなDjangoプロジェクトでは、(設定ファイルを除けば)主に5つのファイルがあるだけ、という感覚です。urls.pymodels.pyviews.pyadmin.py、そしてtests.pyです。他のもの、たとえばHTMLテンプレートがどこにあるか知りたくなっても、たいていはその5つのどれかから明示的に参照されているので見つけられます。

組み込みの管理画面

今回のプロジェクトでは、データベース内のデータを手動で編集したり閲覧したりするための管理画面が欲しいと思っていました。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は気に入っていて、__JOINを表現するところがクールだなと思っています。たとえばこんな感じです。

Zine.objects
    .exclude(product__order__email_hash=email_hash)

このクエリには5つのテーブルが関わっています。zineszine_productsproductsorder_products、そしてordersです。これを動かすために私がやったのは、「orders」と「products」を結びつけるManyToManyFieldと、「zines」と「products」を結びつけるもう一つのManyToManyFieldがあることをDjangoに教えてあげるだけでした。そうすればDjangoが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を使うにあたっては、こちらの手順に従っています。

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

こういうのがあると、他に何か基本的なウェブサイトの機能が欲しくなったときも、きっとDjangoに最初から簡単なやり方が用意されているんだろうな、という気分になります。

settingsファイルはまだ手強く感じる

settings.pyファイルには今でもちょっと圧倒されています。Djangoの設定システムは、ファイル内でたくさんのグローバル変数を設定することで動くのですが、「もし変数名をタイプミスしたらどうしよう?どうやって気づけばいいの?たとえばWSGI_APPLICATIONと書くべきところをWSGI_APPLICATOIN = "config.wsgi.application"と書いてしまったら?」といったことが少しストレスに感じます。

Pythonの言語サーバーがタイプミスを教えてくれるのに慣れてしまっているので、そこに頼れないとなるとちょっと戸惑ってしまうのかもしれません。

とりあえず今回はここまで!

これまでプロジェクトでちゃんとしたWebフレームワークを使いこなせたことがなく(今のところ私のウェブサイトはほとんどが単一のGoバイナリか静的サイトです)、これからどうなるのか楽しみです!

まだ学ぶことはたくさんあって、Djangoのフォームバリデーションや認証システムについても、まだちゃんと触れられていません。

ORMを試してみる気にさせてくれたMarco Rogersに感謝します。

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

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント