Web開発
Djangoについて私が楽しんでいること、さらにいくつか
Some more things about Django I've been enjoying (jvns.ca)
要約
著者は、SQLデータベースとバックエンドレンダリングを伴う「2010年代風」のウェブサイト構築をDjangoで学んでいます。特に、クエリビルダー、テンプレートフィルター、自動データベースマイグレーションといった機能に満足していますが、ビューでのコード整理における継承の利用や、Djangoサイトのパフォーマンス最適化については課題を感じています。
全文翻訳
こんにちは!私は今、SQLデータベースを持ち、バックエンドでHTMLをレンダリングするという、ある種の2010年代スタイルでウェブサイトを作る方法を学んでいます。これは私にとって、必ずしも「簡単」に感じられる旅ではありません。なぜなら、2000年代や2010年代にその方法を学んだことがなく、学ぶべきことがたくさんあるからです。そこで、Goの標準ライブラリやFlaskを使おうとして失敗したときよりも、この種のサイト構築をより実現可能に感じさせてくれるDjangoの機能についていくつか紹介します。また、Djangoで遭遇したいくつかの問題点についても触れます。
なぜ2010年のようなウェブサイトの作り方を学ぶのか?
以前、私がウェブサイトを作る上で自信を持てたツールキットは以下の通りでした。
静的サイトジェネレーター(このブログのようなもの)
JavaScriptで楽しいことをする静的サイト(このSQLプレイグラウンドのようなもの)
Lambdaをバックエンドとするか、GoバックエンドとするシンプルなVue.jsシングルページアプリ(mess with dns のようなもの)
これらの非常にシンプルなアプリケーションでは、このフロントエンド中心のアプローチが気に入っていましたが、多くの異なるページを持つもの(文字通り1ページではなく)を作ることを考え始めたとき、多くのフロントエンドコードを伴う選択肢にはあまり興奮を感じませんでした。そこで、バックエンドを試してみることにしました。JavaScriptをできるだけ少なく使うバックエンド中心のサイトを書くことは、バックエンドでできるだけ少なく行うシングルページJSサイトを書くことと、ある意味では同じように感じられます。たとえそれらが反対のように見えても。どちらの場合も、ロジックの大部分を1つの場所に留めようとしています。
さて、Djangoについての考えです!
クエリビルダーを楽しんでいます
Djangoで、クエリを構築する際に使用する可能性のあるさまざまなWHEREステートメントを持つメソッドのセットを定義できる「クエリセット」クラスを定義できることを学びました。
以下は、メソッドの意味をすべて定義した後、ビューコードでどのように使用するかです。
Events.objects.approved()
.for_tab(tab)
.with_festivals(tab_params.festival_slugs)
.is_free(tab_params.free)
.is_outdoors(tab_params.outdoors)
そして、メソッドの定義方法は以下の通りです。
class EventQuerySet(SearchableQuerySetMixin, models.QuerySet):
def approved(self):
return self.filter(approved_at__isnull=False)
def future(self):
today = timezone.localdate()
return self.filter(end__gt=self._midnight(today))
def with_tags(self, tags):
if tags:
return self.filter(tags__name__in=tags).distinct()
return self
フィルターを定義する構文は私の好みではありませんが、ほとんどの時間をメソッドの使用に費やしており、非常に読みやすく使いやすいと感じています。そして、将来的に他のクエリビルダーライブラリを調べる意欲を掻き立てられます。過去には「SQLを知っているのに、誰がクエリビルダーを必要とするんだ?」と思っていましたが、この種の構造は本当に読みやすくしてくれます。Pythonで独自の小さなクエリビルダーを書いた人の例を見つけましたが、よりミニマルなバージョンを使用することを楽しむことができるかどうかを考えるために、後で読みたいと思っています。
テンプレートフィルターは素晴らしいです
Djangoテンプレートには、HTML生成に非常に役立つ、ちょっとした品質向上のためのフィルターがたくさんあります。これまでに使用したものは次のとおりです。
プレーンテキストをリンクに変換したり、改行を<br>に変換したりします({{ event.description|urlize|linebreaksbr }})
日付のフォーマット({{ row.date|date:"M j" }})
json_scriptは、Python辞書を受け取り、それを自動的にJSONに変換して、安全な方法でHTMLの<script>タグに挿入します。
これらはすべて個別に小さなことですが、それらが利用可能であるだけで、何らかの形で大きな違いを生むと感じています。
クエリ文字列はクールです
私の最も好きなテンプレートフィルターは、おそらくクエリ文字列です。
このサイトでは、表示されるものを決定するために、?date=2026-06-01のようなフィルターを使用することがあります。クエリ文字列は、次のような変更を加えて同じクエリ文字列へのリンクを作成します。
<a href="{% querystring date=nav.prev_date%}">前の日付へのリンク
または、outdoors パラメータを削除するには:
<a href="{% querystring outdoors=None %}">アウトドアパラメータを削除
自動データベースマイグレーションは依然として素晴らしいです
私はDjangoの自動データベースシステムが依然として大好きです。モデルを編集して新しいフィールドを追加するだけで、Djangoが自動的にマイグレーションを生成できるのは驚くべきことです。これまでに19回のデータベースマイグレーションを行いましたが、おそらくもっと増えるでしょう!問題に対する理解が変わるにつれて、データベースを簡単に変更できることは、私にとって大きな違いとなります。
継承でコードを整理したくありません
Djangoのドキュメントでは、ビューのコードを整理するためにクラスベースビューと継承を使用するオプションが提供されることがあります。例えば、多くのコードを共有する4つのビューがあり、親クラスのようなものを定義して、他のビューがそこから継承するようにすることで、それを管理するために継承を使用できます。試してみましたが、ビュー間でコードを共有するために継承を使用する経験は楽しめませんでした。代わりに、この投稿が推奨しているような関数を使用するように切り替えましたが、それははるかに簡単でした。Pythonで継承を使って良い経験をしたことは一度もなく、もう試すつもりはないと思います。しかし、Django自体が提供するインターフェースを使用するために継承を使用することには抵抗がありません。例えば、クエリセットを定義したい場合、次のようなものを書く必要があります。
class EventQuerySet(SearchableQuerySetMixin, models.QuerySet)
あまり深く考えず、うまくいっているようです。(メタコメントとして:プログラミングに関する意見を、「THINGは私にとって気分が良くない、私はOTHER THINGを代わりに好む」と言うことで話すようにしています。リンクした投稿では、関数ベースビューが「正しい方法」であると述べています。「正しい」かどうかにはあまりこだわりませんが、他の人も継承について私と同じように感じていると知るのは心強いです。)
Djangoのパフォーマンスについてどう考えればいいかわかりません
ある時点で、LLMスクレイパーが私たちのサイトを発見し、毎秒約10リクエストを送信し始めました。それらをブロックしましたが、今のところ機能しています。しかし、それはサイトの容量について私に考えさせました。私はGoのバックエンドを書くことに慣れており、パフォーマンスの状況は非常に明確です(通常、すべてが十分に高速です)。そして、Djangoサイトは非常に異なります。軽い負荷テスト((ab -n 1000 -c 1)を使用)によると、現在、約2〜3リクエスト/秒(約10ドル/月のVMで)を処理できます。プロファイリングをして何が遅いのかを突き止め、それを速くしようとするという、うさぎの穴に落ちていくのは魅力的です(それにはpy-spyがあり、py-spyは素晴らしくて非常に使いやすいです。そして、プロファイリングは楽しいです!)しかし、Djangoサイトに期待すべきパフォーマンスについて、そして高レベルでどのように考えるべきかについて、私は本当に理解していません。まだ解決していないことがいくつかあります。
時折トラフィックの急増が発生するサイトがある場合、スケールアップする必要がありますか?より多くのものをキャッシュできるようにサイトを設計したいですか?(そして、本当にそうする必要がありますか?キャッシュは正しく設定するのが非常に面倒です!)
Djangoのパフォーマンスドキュメントによると、Jinjaはテンプレート処理においてより高速であるとのことですが、テンプレートシステムを切り替えることを考えるべきでしょうか?それらのドキュメントには、「{% block %} は {% include %} を使用するよりも高速です」とも書かれています。大きな違いがあるのか、そしてなぜテンプレートキャッシュが重要なのか疑問に思います。
Djangoについて学んでいることの1つは、それがフレームワーク(tm)であるため、誤って設定を間違えやすいということです。例えば、なぜ私のサイトが遅いのかと考えていたとき、Djangoのパフォーマンスドキュメントを読み、次のようなコメントに気づきました。
キャッシュされたテンプレートローダーを有効にすると、各テンプレートがレンダリングされるたびにコンパイルするのを回避できるため、パフォーマンスが劇的に向上することがよくあります。
CPUプロファイリングを行ったとき、テンプレートのレンダリングに多くの時間を費やしていることに気づきました!これは私を助けるかもしれません!リンクをクリックすると、キャッシュされたテンプレートローダーはデフォルトでオンになっているはずでしたが、何か別のことをしようとしている間に誤ってオフにしていました。この「デフォルトでキャッシュされたテンプレートローダーをオフにした」ということが、私がまだdjaを見つける方法の例だと思います。