Djangoのハイライト:テンプレートによる保存行(パート2)
公開: 2022-03-10Webサイトを構築するための飾り気のないアプローチの中には、開発者がHTMLのすべての行を手作業で作成する必要があるものがあります。 もう一方の極端な例として、商用のノーコードサイトビルダーは、ユーザーのすべてのHTMLを自動的に作成しますが、多くの場合、結果のコードの可読性が犠牲になります。 テンプレートはその範囲のほぼ中間ですが、たとえばReactまたは同様のライブラリを使用してシングルページアプリケーションでページ構造を生成するよりも、手書きのHTMLに近いものです。 連続体のこのスイートスポットは、ゼロからの手動HTML(セマンティック/読み取り可能なコード、ページ構造の完全な制御、高速ページの読み込み)の多くの利点を提供し、関心の分離と簡潔さを追加します。手作業で変更されたHTML。 この記事では、Djangoテンプレートを使用して複雑なページを作成する方法について説明します。
今日のトピックは、Djangoフレームワークを超えて適用されます。 Flask(別のWebフレームワーク)とPelican(静的サイトジェネレーター)は、テンプレート作成に同じアプローチを使用する他の多くのPythonプロジェクトのほんの2つです。 Jinja2は、3つのフレームワークすべてが使用するテンプレートエンジンですが、プロジェクト設定を変更することで別のフレームワークを使用できます(厳密に言えば、Jinja2はDjangoテンプレートのスーパーセットです)。 これは、フレームワークがなくても独自のプロジェクトに組み込むことができる独立したライブラリであるため、この記事の手法は広く役立ちます。
シリーズの前の部分:
- Djangoのハイライト:ユーザーモデルと認証(パート1)
- Djangoのハイライト:モデル、管理、およびリレーショナルデータベースの活用(パート3)
- Djangoのハイライト:静的アセットとメディアファイルのラングリング(パート4)
サーバーサイドレンダリング
テンプレートは、HTMLが追加の記号で拡張された単なるHTMLファイルです。 HTMLの略を覚えておいてください: H yper T ext M arkup Language。 テンプレート言語であるJinja2は、意味のあるマークアップ記号を追加して言語に追加するだけです。 これらの追加の構造は、サーバーがテンプレートをレンダリングしてユーザーにプレーンなHTMLページを提供するときに解釈されます(つまり、テンプレート言語からの追加のシンボルは最終出力になりません)。
サーバー側のレンダリングは、要求に応じてWebページを構築するプロセスです。 Djangoは、サーバー側のレンダリングを使用してHTMLページをクライアントに提供します。 ビュー関数は、実行の最後に、HTTPリクエスト、1つ以上のテンプレート、およびオプションで関数の実行中にアクセスされるデータを組み合わせて、クライアントへの応答として送信する単一のHTMLページを作成します。 データはデータベースからビューを経由してテンプレートに送られ、ユーザーに送られます。 この抽象的な説明が完全に意味をなさなくても心配しないでください。この記事の残りの部分では、具体的な例に目を向けます。
セットアップ
この例では、かなり複雑なWebページであるBootstrapの管理用テンプレートを起動し、HTMLをJinja2テンプレートとして書き直します。 MITライセンスのライブラリは(JavaScriptとPugに基づく)異なるテンプレートシステムを使用して表示されるページを生成しますが、そのアプローチはJinja2スタイルのテンプレートとは大幅に異なるため、この例は翻訳というよりもリバースエンジニアリングに近いことに注意してください。彼らの優れたオープンソースプロジェクトの作成するWebページを確認するには、StartBootstrapのライブプレビューをご覧ください。
この記事のサンプルアプリケーションを用意しました。 自分のコンピューターでDjangoプロジェクトを実行するには、Python 3仮想環境を起動してから、次のコマンドを実行します。
pip install django git clone https://github.com/philipkiely/sm_dh_2_dashboard.git cd sm_dh_2_dashboard python manage.py migrate python manage.py createsuperuser python manage.py loaddata employee_fixture.json python manage.py runserver
次に、Webブラウザーを開き、 https://127.0.0.1:8000
に移動します。 下の画像と一致するプレビューと同じページが表示されます。
このチュートリアルはフロントエンドに焦点を当てているため、基盤となるDjangoアプリは非常にシンプルです。 これは、単一のWebページを表示するための多くの構成のように見えるかもしれませんが、公平を期すためにそうです。 ただし、この多くの設定は、はるかに堅牢なアプリケーションもサポートする可能性があります。
これで、この668行のHTMLファイルを適切に設計されたDjangoサイトに変換するプロセスを実行する準備が整いました。
テンプレートと継承
数百行のHTMLをクリーンなコードにリファクタリングする最初のステップは、要素を独自のテンプレートに分割することです。これは、レンダリングステップ中にDjangoが単一のWebページに構成します。
ページ/テンプレートをご覧ください。 5つのファイルが表示されます。
- base.html 、すべてのWebページが拡張するベーステンプレート。 タイトル、CSSインポートなどの
<head>
が含まれています。 - navbar.html 、上部ナビゲーションバーのHTML、必要に応じて含まれるコンポーネント。
- footer.html 、ページフッターのコード、必要に応じて含まれる別のコンポーネント。
- sidebar.html 、サイドバーのHTMl、必要に応じて含まれる3番目のコンポーネント。
- index.html 、メインページに固有のコード。 このテンプレートは基本テンプレートを拡張し、3つのコンポーネントを含みます。
Djangoは、Voltronのようにこれらの5つのファイルをアセンブルして、インデックスページをレンダリングします。 これを可能にするキーワードは、 {% block %}
、 {% include %}
、および{% extend %}
です。 base.html :
{% block content %} {% endblock %}
これらの2行は、 base.htmlを拡張して独自のHTMLを挿入する他のテンプレート用のスペースを残します。 content
は可変名であることに注意してください。テンプレートには異なる名前の複数のブロックを含めることができ、子テンプレートに柔軟性を与えることができます。 index.htmlでこれを拡張する方法を確認します。
{% extends "base.html" %} {% block content %} <!-- HTML Goes Here --> {% endblock %}
基本テンプレート名でextends
キーワードを使用すると、インデックスページにその構造が与えられ、見出しをコピーする手間が省けます(ファイル名は二重引用符で囲まれた文字列形式の相対パスであることに注意してください)。 インデックスページには、サイトのほとんどのページに共通する3つのコンポーネントがすべて含まれています。 以下のように、 include
タグを使用してこれらのコンポーネントを取り込みます。
{% extends "base.html" %} {% block content %} {% include "navbar.html" %} {% include "sidebar.html" %} <!--Index-Specific HTML--> {% include "footer.html" %} <!--More Index-Specific HTML--> {% endblock %}
全体として、この構造には、ページを個別に作成するよりも3つの重要な利点があります。
- DRY(Do n't Repeat Yourself)コード
共通のコードを特定のファイルに分解することで、1つの場所でのみコードを変更し、それらの変更をすべてのページに反映させることができます。 - 読みやすさの向上
1つの巨大なファイルをスクロールするのではなく、関心のある特定のコンポーネントを分離できます。 - 関心事の分離
たとえば、サイドバーのコードは1つの場所に配置する必要があります。コードの下部に不正なscript
タグが浮かんでいることや、個別のコンポーネント間で他の要素が混ざり合っていることはありません。 個々の部分を因数分解すると、この優れたコーディング手法が必要になります。
特定のコンポーネントをbase.htmlテンプレートに配置することで、さらに多くのコード行を節約できますが、それらを別々に保つことには2つの利点があります。 1つ目は、単一のブロック内のどこに属するかを正確に埋め込むことができることです(これは、 content
ブロックのメインdiv
内にあるfooter.htmlにのみ関連します)。 もう1つの利点は、404エラーページなどのページを作成し、サイドバーやフッターが不要な場合は、それらを省略できることです。
これらの機能は、テンプレート作成のコースと同等です。 次に、 index.htmlで使用できる強力なタグを使用して、動的な機能を提供し、数百行のコードを節約します。
2つの基本的なタグ
これは、利用可能なタグの完全なリストからはほど遠いものです。 テンプレートに関するDjangoのドキュメントは、そのような列挙を提供します。 今のところ、テンプレート言語の最も一般的な2つの要素のユースケースに焦点を当てています。 私自身の仕事では、基本的にfor
タグとif
タグのみを定期的に使用していますが、提供されている他の12個以上のタグには独自のユースケースがあり、テンプレートリファレンスで確認することをお勧めします。
タグに入る前に、構文についてメモしておきたいと思います。 タグ{% foo %}
は、「foo」がテンプレートシステム自体の関数またはその他の機能であることを意味し、タグ{{ bar }}
は、「bar」が特定のテンプレートに渡される変数であることを意味します。
Forループ
残りのindex.htmlでは、数百行のコードの最大のセクションがテーブルです。 このハードコードされたテーブルの代わりに、データベースから動的にテーブルを生成できます。 セットアップ手順からpython manage.py loaddata employee_fixture.json
を呼び出します。 このコマンドは、Django Fixtureと呼ばれるJSONファイルを使用して、57の従業員レコードすべてをアプリケーションのデータベースにロードしました。 views.pyのビューを使用して、このデータをテンプレートに渡します。
from django.shortcuts import render from .models import Employee def index(request): return render(request, "index.html", {"employees": Employee.objects.all()})
render
3番目の位置引数は、テンプレートで使用できるようになるデータの辞書です。 このデータとfor
タグを使用してテーブルを作成します。 このWebページを適応させた元のテンプレートでも、従業員のテーブルはハードコーディングされていました。 私たちの新しいアプローチは、ハードコードされた繰り返しのテーブル行の数百行をカットします。 index.htmlに含まれるもの:
{% for employee in employees %} <trv <td>{{ employee.name }}</td> <td>{{ employee.position }}</td> <td>{{ employee.office }}</td> <td>{{ employee.age }}</td> vtd>{{ employee.start_date }}</td> <td>${{ employee.salary }}</td> </tr> {% endfor %}
より大きな利点は、これによりテーブルの更新プロセスが大幅に簡素化されることです。 開発者がHTMLを手動で編集して昇給や新入社員を反映し、その変更を本番環境にプッシュするのではなく、管理者は管理パネルを使用してリアルタイムの更新を行うことができます(https://127.0.0.1/admin、アクセスするためにpython manage.py createsuperuser
で作成した資格情報)。 これは、静的サイトジェネレーターやその他のテンプレートアプローチでDjangoを単独で使用する代わりに、このレンダリングエンジンでDjangoを使用することの利点です。
それ以外の場合
if
タグは非常に強力なタグであり、テンプレート内の式を評価し、それに応じてHTMLを調整できます。 {% if 1 == 2 %}
のような行は、毎回同じ結果と評価されるため、少し役に立たない場合でも、完全に有効です。 if
タグが光るのは、ビューによってテンプレートに渡されたデータを操作するときです。 sidebar.htmlの次の例を考えてみましょう。
<div class="sb-sidenav-footer"> <div class="small"> Logged in as: </div> {% if user.is_authenticated %} {{ user.username }} {% else %} Start Bootstrap {% endif %} </div>
これを実現するためにビューで何も指定せずに、デフォルトでユーザーオブジェクト全体がテンプレートに渡されることに注意してください。 これにより、ユーザーの認証ステータス(またはその欠如)、ユーザー名、およびその他の機能にアクセスできます。これには、外部キーの関係に従って、ユーザープロファイルまたはその他の接続モデルに保存されているデータにすべてHTMLファイルからアクセスできます。
このレベルのアクセスがセキュリティリスクをもたらす可能性があることを懸念するかもしれません。 ただし、これらのテンプレートはサーバー側のレンダリングフレームワーク用であることに注意してください。 ページを作成した後、タグはそれ自体を消費し、純粋なHTMLに置き換えられます。 したがって、 if
ステートメントが特定の条件下でページにデータを導入するが、そのデータが特定のインスタンスで使用されない場合、 if
ステートメントはサーバー側で評価されるため、そのデータはクライアントにまったく送信されません。 つまり、適切に構築されたテンプレートは、必要な場合を除いて、機密データをサーバーから送信せずにページに機密データを追加するための非常に安全な方法です。 とはいえ、Djangoテンプレートを使用しても、機密情報を安全で暗号化された方法で通信する必要がなくなるわけではありません。つまり、 user.is_authenticated
のようなセキュリティチェックは、サーバー側で処理されるときにHTMLで安全に実行できます。
この機能には、他にも多くのユースケースがあります。 たとえば、一般的な製品のホームページでは、「サインアップ」ボタンと「サインイン」ボタンを非表示にして、ログインユーザーの「サインアウト」ボタンに置き換えることができます。 もう1つの一般的な使用法は、フォーム送信などの操作の成功メッセージまたはエラーメッセージを表示および非表示にすることです。 通常、ユーザーがログインしていない場合はページ全体を非表示にしないことに注意してください。ユーザーの認証ステータスに基づいてWebページ全体を変更するより良い方法は、 views.pyの適切な関数で処理することです。
フィルタリング
ビューの仕事の一部は、ページに合わせてデータを適切にフォーマットすることです。 これを実現するために、タグの強力な拡張機能であるフィルターがあります。 Djangoには、テキストの位置揃え、日付の書式設定、数値の追加などのアクションを実行するために使用できる多くのフィルターがあります。 基本的に、フィルターはタグ内の変数に適用される関数と考えることができます。 たとえば、給与番号を「1200000」ではなく「$ 1,200,000」に設定します。 フィルタを使用して、 index.htmlで作業を実行します。
<td>${{ employee.salary|intcomma }}</td>
パイプ文字|
intcomma
コマンドをemployee.salary
変数に適用するフィルターです。 「$」文字はテンプレートからのものではありません。毎回表示されるような要素の場合は、タグの外側に貼り付ける方が簡単です。
intcomma
では、 settings.pyのINSTALLED_APPS
のindex.htmlと'django.contrib.humanize',
先頭に{% load humanize %}
を含める必要があることに注意してください。 これは、提供されているサンプルアプリケーションで行われます。
結論
Jinja2エンジンを使用したサーバー側のレンダリングは、クリーンで適応性があり、応答性の高いフロントエンドコードを作成するための主要なツールを提供します。 ページをファイルに分割することで、柔軟な構成のDRYコンポーネントが可能になります。 タグは、ビュー機能によってデータベースから渡されたデータを表示するための基本的な機能を提供します。 正しく行われると、このアプローチはサイトの速度、SEO機能、セキュリティ、および使いやすさを向上させることができ、Djangoおよび同様のフレームワークでのプログラミングのコアな側面です。
まだ行っていない場合は、サンプルアプリケーションを確認し、完全なリストを使用して独自のタグとフィルターを追加してみてください。
Django Highlightsは、DjangoでのWeb開発の重要な概念を紹介するシリーズです。 各記事は、フロントエンドの開発者と設計者がコードベースの「残りの半分」をより深く理解できるようにすることを目的とした、Django開発の側面へのスタンドアロンガイドとして書かれています。 これらの記事は主に理論と慣習を理解するのに役立つように作成されていますが、Django3.0で記述されたコードサンプルがいくつか含まれています。
シリーズの前の部分:
- Djangoのハイライト:ユーザーモデルと認証(パート1)
- Djangoのハイライト:モデル、管理、およびリレーショナルデータベースの活用(パート3)
- Djangoのハイライト:静的アセットとメディアファイルのラングリング(パート4)