◀ 7.

Djangoテンプレートのif・elif・else|型・未定義と複合条件

▶

Djangoテンプレートの条件分岐は{% if 条件 %}、{% elif 条件 %}、{% else %}、{% endif %}で記述します。Pythonのif文やPHP/JavaScriptの構文をテンプレートへ貼るのではありません。テンプレート配下のこの記事では、表示する内容の選び方と、値の型・未定義・複合条件の注意を説明します。

Djangoテンプレートでif、elif、elseを順に判定し最初の成立箇所だけを表示する図

元のif・elif・else例

元記事の黒いコード表を復元しています。error_flagが数値の1なら「エラー」、そうでなくvalが数値の0なら「正常」、どちらでもなければ「想定外」です。複数の条件が成立しても最初に成立した箇所だけが表示されます。Djangoテンプレートにendifが必要で、Pythonのようにコロンやインデントだけでは閉じられません。

テンプレートに以下の記述をすることで条件分岐させることができます。

{% if error_flag == 1 %}
    エラー
{%
elif val == 0 %}
    正常
{%
else %}
    想定外
{%
endif %}

 

同じ内容のコピー用テキストです。error_flag/valはビューからテンプレートのcontextへ渡す値の名前で、決められた組込み変数ではありません。HTTPパラメータの文字列を数値へ勝手に読み替える例でもありません。

{% if error_flag == 1 %}
  エラー
{% elif val == 0 %}
  正常
{% else %}
  想定外
{% endif %}
error_flagval表示理由
1(数値)0(数値)エラーifで成立し、elifへ進まない
0(数値)0(数値)正常ifは不成立、elifで成立
0(数値)2(数値)想定外どちらも不成立
"1"(文字列)"0"(文字列)想定外数値との比較に一致しない
どちらも未定義どちらも未定義想定外この比較では成立しない

truthy、0、None、未定義を分ける

{% if value %}は値の真偽で分岐します。数値0、False、None、空文字、空リスト、未定義変数などはこの判定では偽です。一方、"0"や"False"という空でない文字列は真です。表示が予想と違う場合は、テンプレートの演算子だけでなく、ビューが渡す値の型とキーを確認します。

{% if value %}truthy{% else %}falsy{% endif %}
{% if value is None %}none-or-missing{% else %}not-none{% endif %}
{% if value == 0 %}equal-zero{% else %}not-zero{% endif %}

上の3行は別々の条件です。0ではfalsy/not-none/equal-zero、"0"ではtruthy/not-none/not-zero、Noneや未定義ではfalsy/none-or-missing/not-zeroになります。FalseもPython由来の等価比較では0と等しいためequal-zeroです。==0を「型も必ず整数の0」とする入力検証には使いません。未定義とNoneをis Noneだけで区別できるとも考えないでください。

必要な値を明示的に整形し、欠落とNoneを区別する必要があるなら、ビュー側で別のフラグや表示用データへ整理します。ただし、表示の都合で処理した値をそのまま保存・課金・権限判定の入力検証へ流用しません。

and・or・notと括弧の注意

Djangoテンプレートではand/or/notを使い、&&/||/!やPHPのelseifは使いません。andはorより優先されるため、enabled and is_staff or is_ownerは、is_ownerだけが真でも成立します。enabled and (is_staff or is_owner)のような実際の丸括弧はifタグでは使えません。両方のグループが必要なときはネストして明示します。

{% if enabled %}
  {% if is_staff or is_owner %}
    操作ボタンを表示
  {% endif %}
{% endif %}

この例ではenabledが偽ならis_ownerが真でも表示しません。複雑な条件を何段もテンプレートへ書くより、ビュー/サービス側でcan_show_controlsのような表示用のboolを組み立てて渡すと読みやすくなります。ただし表示用フラグ自体が操作の認可を実装するわけではありません。

比較・所属・フィルタ

  • ==/!=/</>/<=/>=で比較できますが、Pythonのコード全体が実行できるわけではありません。変数の型が異なる場合や欠落時も含め、期待する入力を明確にします。
  • in/not inは所属判定に使えます。文字列に対しては部分文字列、dictに対してはキーの有無なので、同じ「含む」でも対象が異なります。
  • is/is notは同一性の判定です。特にis Noneを==0や空判定の置換に使いません。ifタグの比較をPHPの===へ置き換えることもできません。
  • フィルタを条件に使えますが、defaultは偽と評価される値全般を置換します。default_if_noneはNoneを対象とし、空文字・0・Falseを同じ意味で置換するものではありません。
  • a < b < cの連鎖比較はPythonと同じ書き方にせず、a < b and b < cに分けます。高価なQuerySetの評価やメソッドの自動呼出しを含む値は、表示のためだけの意図しないDB処理も確認します。

表示条件とアクセス制御は別

{% if user.is_authenticated %}でログイン済み向け表示を切り替えても、対象URLへの直接アクセスやPOSTを拒否する認可にはなりません。サーバー側でも認証・操作権限・所有者や対象の範囲を検査します。HTMLに出さない秘密値を、非表示CSSやコメントで埋め込むこともしません。

ifで選んだ文字列の出力にもエスケープは必要です。通常のHTMLテキスト出力ではDjangoの自動エスケープを維持し、表示が崩れた対処としてsafeやautoescape offを追加しません。HTMLのエスケープをJavaScript/CSS/URL等の全ての文脈に万能と考えることもできません。ユーザーの入力文字列をテンプレートソースとしてコンパイルしないでください。

うまく分岐しないとき

  1. 実際に読み込まれるテンプレートの探索先・継承先を確認する。同名ファイルを別の場所で編集していないか照合する。
  2. contextのキーと型を確認する。数値0と文字列"0"、boolと文字列"False"、空と未定義を分ける。個人情報やrequest全体を公開ページ/ログへ出さない。
  3. endifの対応、elif/elseの順序、演算子の前後のスペースを確認する。if x==1のように演算子を詰めない。
  4. 括弧やPythonの関数呼出し・三項式をそのまま書いていないか確認する。DTLとJinja2も別のエンジンであり、同じ構文とは限らない。
  5. 正常・異常・境界・欠落のcontextで最小テンプレートを描画して期待結果と比較する。公開サイトで入力や権限を変えて試験しない。

検証範囲と公式資料

2026年10月26日、Django5.2.18/Python3.12.10の独立Engine/Contextで掲載テンプレートを描画し、元例の優先順位、型/未定義/None/空値、ネスト・演算子・不正構文・HTML自動エスケープを検査しました。実プロジェクトのloader/ビュー/業務認可、DB QuerySetや実ブラウザ、Jinja2の動作検証ではありません。親のテンプレート一覧から他の子ページを選べます。

公式資料:Djangoのifタグと演算子、defaultフィルタ、default_if_none、HTMLの自動エスケープ。以前の他言語の一般解説は過去版に残し、ここではDjangoテンプレートの用途へ戻しています。

子ページ

子ページはありません

同階層のページ
  1. Templateの使用準備
  2. Template の定義方法
  3. テンプレートの作成と共通化
  4. setting.pyにおけるテンプレートの設定
  5. テンプレートの名前の重複について
  6. 静的ファイルの読み込み
  7. if文
  8. テンプレートで定数を使用する方法
  9. aタグのhrefの記載方法

最近更新/作成されたページ