Djangoテンプレート構文と使用方法
Djangoテンプレート言語の紹介(公式ドキュメントより)
Djangoのテンプレート言語は、機能性と使いやすさのバランスを取ることを目指しています。HTMLに慣れ親しんだ人にとっては、非常に快適な環境です。SmartyやJinja2などの他のテキストベースのテンプレート言語に触れたことがある場合は、Djangoのテンプレートにすぐに馴染めるでしょう。
哲学
プログラミングの背景がある場合、またはHTMLにプログラムコードを直接組み込む言語に慣れている場合は、Djangoテンプレートシステムが単にHTMLに埋め込まれたPythonではないことを覚えておく必要があります。これは表現のための設計であり、プログラムロジックのためのものではありません。
Djangoテンプレートシステムは、if、ブール値、forループなどのタグ機能を提供しますが、これらのタグは対応するPythonコードを単純に実行するわけではありません。テンプレートシステムは任意のPython式を実行しません。デフォルトでは、以下にリストされているマークアップ、フィルター、および構文のみがサポートされています(テンプレート言語に独自の拡張を追加することもできます)。
哲学
なぜXMLベースのテンプレート(ZopeのTALなど)ではなくテキストベースのテンプレートを使用するのか?私たちはDjangoのテンプレート言語がXML/HTMLテンプレートだけでなく、電子メール、JavaScript、CSVなどにも使用できることを望んでいます。テンプレート言語は、テキストベースのあらゆる形式に使用できます。
もう一つ:XMLを人間が編集するのは虐待です!
テンプレートとは?
テンプレートは単なるテキストファイルです。HTML、XML、CSVなど、あらゆるテキストベースの形式を生成できます。テンプレートには、評価時に値に置き換えられる変数が含まれており、変数にはテンプレートのロジックを制御するマークアップが含まれています。HTML内にテンプレート構文が含まれている場合、そのファイルはテンプレートと呼ばれます。
テンプレートのコメント
テンプレートのコメントは以下のように記述します:
{# {{ '10'|add_str:'5' }} #}
{# コメント内容 #}
変数 {{ 変数 }}
変数:構文は {{ }} です。括弧内にレンダリングする変数値を記述します。変数名は文字、数字、アンダースコアで構成されます。
コード例
# views.pyの関数
def template_test(request):
name = '鋼蛋'
age = 18
hobby = ['歌', '踊り', 'ラップ', 'バスケットボール']
empty_list = []
empty_string = ''
data = {
'name': '鉄蛋',
'age': 16,
'hobby': hobby,
'key': 'xxxxx'
}
empty_dict = {}
return render(request, 'template_test.html', {
'name': name,
'age': age,
'hobby': hobby,
'empty_list': empty_list,
'empty_string': empty_string,
'data': data,
'empty_dict': empty_dict
})
# テンプレートファイル(template_test.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>テンプレートテスト</title>
</head>
<body>
<p>{{ name }}</p>
<p>{{ age }}</p>
<p>{{ hobby }}</p>
<p>{{ empty_list }}</p>
<p>{{ empty_string }}</p>
<p>{{ data }}</p>
<p>{{ empty_dict }}</p>
</body>
</html>
ブラウザ表示結果
小結:{{ }}内にレンダリングする変数を記述します。標準的な記法は {{ 変数 }} です。バックエンドからパラメータが渡されない場合は表示されません。
ドット(.)の特別な意味
テンプレート言語ではドット(.)は特別な意味を持ち、オブジェクトの対応する属性値を取得するために使用されます。
例:インデックス、キー、属性、メソッド
# views.pyの関数
def template_test(request):
numbers = [1, 2, 3, 4, 5]
dictionary = {"name": "黒蛋"}
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def dream(self):
return f"私は{self.name}、{self.age}歳です..."
person1 = Person(name="鋼蛋", age=18)
person2 = Person(name="狗蛋", age=17)
person3 = Person(name="鉄蛋", age=16)
person_list = [person1, person2, person3]
return render(request, "template_test.html", {"numbers": numbers, "dictionary": dictionary, "person_list": person_list})
# テンプレートファイル
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ドットのテスト</title>
</head>
<body>
<p>{{ numbers.0 }}</p> <!--リストの最初の要素-->
<p>{{ dictionary.name }}</p><!--辞書のキー値-->
<p>{{ person_list.0.name }}</p><!--オブジェクトのname属性-->
<p>{{ person_list.0.dream }}</p><!--.操作はパラメータなしのメソッドを呼び出すことができます-->
</body>
</html>
小結
注:テンプレートシステムが(.)に遭遇した場合、以下の順序で検索します:
- 辞書内を検索
- 属性またはメソッド
- 数値インデックス(インデックスは負数にできません)
タグ {% %} - ロジック関連の操作
コード例
{% for item in items %}
{% if condition %}
<!--条件分岐-->
{% elif another_condition %}
<!--別の条件分岐-->
{% else %}
<!--条件が成立しない場合の処理-->
{% endif %}
<!--ifの終了-->
{% endfor %}
<!--forループの終了-->
if文のサポート
if文は and、or、==、>、<、!=、<=、>=、in、not in、is、is not の判断をサポートしています。
注意事項
Djangoのテンプレート言語は連続した判断をサポートしていません。以下の書き方もサポートしていません:
{% if a > b > c %}
...
{% endif %}
Djangoのテンプレート言語では、属性の優先度がメソッドよりも高くなります。
forループのパラメータ
| 変数 | 説明 |
|---|---|
| `forloop.counter` | 現在のループインデックス(1から開始) |
| `forloop.counter0` | 現在のループインデックス(0から開始) |
| `forloop.revcounter` | 現在のループの逆順インデックス(1で終了) |
| `forloop.revcounter0` | 現在のループの逆順インデックス(0で終了) |
| `forloop.first` | 現在のループが最初のループかどうか(ブール値) |
| `forloop.last` | 現在のループが最後のループかどうか(ブール値) |
| `forloop.parentloop` | このループの外側のループ |
for ... empty
forタグには、指定されたグループが空の場合や見つからない場合に操作できる{% empty %}句がオプションで付属しています。
{% for person in person_list %}
<p>{{ person.name }}</p>
{% empty %}
<p>申し訳ありませんが、ここに人はいません</p>
{% endfor %}
フィルター
フィルターは変数の表示結果を変更するために使用されます。構文:{{ value|filter_name:parameter }}。':'の前後にはスペースを入れないでください。
割り算の判定 divisibleby:パラメータ
withの使用
中間変数を定義します(エイリアスを付け、with内でのみ有効)。
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
{% with business.employees.count as total %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
default - システムのデフォルト値
{{ value|default:"nothing"}}
valueの値が渡されない場合はnothingが表示されます。
注:TEMPLATESのOPTIONSでstring_if_invalid:'見つかりません'というオプションを追加できます。これはdefaultの機能を置き換えることができます。
(settings.pyで設定)
filesizeformat - ファイルサイズの人間的な表示
値を「人間が読める」ファイルサイズにフォーマットします(例:'13 KB', '4.1 MB', '102 bytes'など)。
{{ value|filesizeformat }}
valueが123456789の場合、出力は117.7 MBになります。
add - 「加算」
変数にパラメータを加算します。文字列はデフォルトでint型に変換を試み、変換できない場合は連結されます。
{{ value|add:"2" }}
valueが数字4の場合、出力結果は6になります。
{{ value|add:"hello" }}
valueが数字666の場合、出力結果は666helloになります。
{{ first|add:second }}
firstが[1,2,3]、secondが[4,5,6]の場合、出力結果は[1,2,3,4,5,6]になります。
lower - 小文字
{{ value|lower }}
upper - 大文字
{{ value|upper }}
title - タイトルケース
{{ value|title }}
ljust - 左揃え
"{{ value|ljust:"10" }}"
rjust - 右揃え
"{{ value|rjust:"10" }}"
center - 中央揃え
"{{ value|center:"15" }}"
length - 長さ
{{ value|length }}
valueの長さを返します。value=['a', 'b', 'c', 'd']の場合、4が表示されます。
slice - スライス
{{value|slice:"2:-1"}}
first - 最初の要素を取得
{{ value|first }}
last - 最後の要素を取得
{{ value|last }}
join - 文字列でリストを連結
Pythonのstr.join(list)と同じです。
{{ value|join:" // " }}
cut - 指定された文字列をすべて削除
valueが'i love you'の場合、出力は'iloveyou'になります。
{{ value|cut:' ' }}
truncatechars - 文字数で切り詰め(...もカウント)
文字列の文字数が指定された文字数より多い場合、切り詰められます。切り詰められた文字列は翻訳可能な省略記号シーケンス("...")で終わります。
{{ value|truncatechars:9}}
date - 日付フォーマット
# バックエンド
import datetime
def date_format(request):
now = datetime.datetime.now()
return render(request, 'date_format.html', {'now': now})
# テンプレート
{{ now|date:"Y-m-d H:i:s"}} # 表示形式:年-月-日 時:分:秒
safe - XSS攻撃防止(エスケープの解除)
DjangoのテンプレートではHTMLタグやJS構文タグが自動的にエスケープされます。これは明らかにセキュリティのためです。しかし、場合によってはこれらのHTML要素がエスケープされないようにしたい場合があります。たとえば、コンテンツ管理システムを作成する場合、バックエンドで追加した記事は修飾されています。これらの修飾はFCKeditorのようなエディタでHTML修飾子が追加されたテキストである可能性があります。自動エスケープすると、HTMLタグを保護するソースファイルが表示されます。DjangoでHTMLの自動エスケープを無効にするには2つの方法があります。単一の変数の場合は、「|safe」フィルターを通じてDjangoにこのコードが安全であり、エスケープする必要がないことを伝えることができます。
カスタムフィルター
標準の組み込みフィルターでは開発要件を満たせない場合、カスタムフィルターを実装して機能を実現できます。
カスタムフィルターの流れ
- app下にtemplatetagsという名前のpythonパッケージを作成します。
- pythonでpyファイルを作成します。ファイル名は任意です(例:my_filters.py)。
- pyファイルに以下を記述します:
from django import template register = template.Library() # 固定の書き方、変更不可
- 関数とデコレータを記述します:
@register.filter
def add_str(value, arg): # 最大2つのパラメータ
return '{}-{}'.format(value, arg)
- ページコード:カスタムsimple_tagとfilterを使用するhtmlファイルで、前に作成したmy_tags.pyをインポートします。
{% load my_tags %} # まずカスタムファイルmy_tagsをインポート
{{ name|add_str:age }} # パラメータは2つしかありません。1つは変数name、もう1つは後ろのパラメータage
カスタム乗算フィルター multiply
# フィルター関数
@register.filter
def multiply(value, arg):
return value * arg
# テンプレート
{% load my_tags %}
<p>{{ 6|multiply:'6' }}</p>
<p>{{ 6|multiply:6 }}</p>
<p>{{ '10'|multiply:5 }}</p>
カスタム除算フィルター divide
# フィルター関数
@register.filter
def divide(value, arg):
return value / arg
# テンプレート
{% load my_tags %}
<p>{{ 6|divide:6 }}</p>
<p>{{ 6|divide:1 }}</p>
カスタム加算フィルター add
# フィルター関数
@register.filter
def add(value, arg):
return value + arg
# テンプレート
{% load my_tags %}
<p>{{ 6|add:6 }}</p>
<p>{{ 6|add:0 }}</p>
カスタムタグ simpletag
カスタムフィルターとの違いは、より多くのパラメータを受け取れること、およびタグの参照に{{% %}}を使用することです。
# simple_tagのコード
@register.simple_tag(name="sum")
def sum_values(a, b, c):
return "{} + {} + {}".format(a, b, c)
# simple tagの使用
{% load app01_demo %}
{# simple tag #}
{% sum "1" "2" "abc" %}
カスタムタグ inclusion_tag
主にHTMLコードのフラグメントを返すために使用されます(動的変数、ページネーション)。
appファイル下にpythonパッケージを作成
パッケージ名は必ずtemplatetagsにする
templatetags内に任意の.pyファイルを作成
関数を記述
HTMLページコード
# ページネーション表示コード
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for i in num %}
<li><a href="#">{{ i }}</a></li>
{% endfor %}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
# ページ1
{% load my_tags %}
{% pagination 5 %}
# ページ2
{% load my_tags %}
{% pagination 1 %}
表示効果
まとめ:
- カスタムフィルター filter:最大2つのパラメータを受け取り、呼び出し時は{{ filter }}を使用
- カスタムタグ simpletag:カスタムフィルターとの違いは、より多くのパラメータを受け取り、タグの参照に{{% %}}を使用
- カスタムタグ inclusion_tag:主にHTMLコードのフラグメントを返すために使用され、タグの参照に{{% %}}を使用
csrf_token - クロスサイトリクエストフォージェリ保護
ページのformタグ内に{% csrf_token %}を記述します。
静的ファイル関連
静的ファイルのエイリアスとファイルパスを結合し、エイリアスが変更されても影響を受けないようにします。
{% load static %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
<script src="{% static "mytest.js" %}"></script>
{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>
テンプレートの継承
テンプレートとは?
通常のHTMLページ。テンプレートページはHTMLページの共通部分を処理し、冗長なコードを避け、重複したHTMLページの記述を減らし、コードの再利用性を高め、コードの修正を容易にします。
継承の書き方
子ページで、ページの最上部に以下の構文を使用してテンプレートを継承します。
{% extends 'base.html' %}
blockブロック
テンプレートで{% block xxx %}を使用して「ブロック」を定義します。子ページでは、テンプレート内のblock名を定義して、テンプレート内の対応する内容を置き換えます。
{% block ブロック名 %}
# コンテンツ
{% endblock %}
子ページによるblockブロックの置き換え
注意点:
- {% extends 'base.html' %}は最初の行に記述します。前に内容があると表示されます。
- {% extends 'base.html' %}の'base.html'にはクォーテーションを付けます。さもないと変数として検索されます。
- 表示する内容はblockブロック内に記述します。
- 複数のblockブロックを定義し、css、jsブロックを定義します。
コンポーネント
よく使われるページコンテンツ(ナビゲーションバー、フッタ情報など)を個別のファイルに保存し、必要な場所で以下の構文を使用してインポートできます。
# 例:単独のhtmlファイル(navigation.html)
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for i in num %}
<li><a href="#">{{ i }}</a></li>
{% endfor %}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
# 他のページでのインポート
{% include 'navigation.html' %}