Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Djangoテンプレートエンジンを使いこなそう!
Search
Shinya Okano
October 06, 2023
Technology
3.1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Djangoテンプレートエンジンを使いこなそう!
Shinya Okano
October 06, 2023
More Decks by Shinya Okano
See All by Shinya Okano
プロファイラを使ってPythonアプリをチューニングしよう
tokibito
2
1.4k
Pythonのデバッガーを使おう
tokibito
1
960
Djangoフレームワークの紹介_OSC北海道2019
tokibito
1
1.1k
DjangoCongressJP開催レポート
tokibito
0
100
Djangoフレームワークの紹介 OSC2018do
tokibito
0
2.9k
Other Decks in Technology
See All in Technology
SRE依存からの脱却 運用を開 発チームへ移す、 フルサイ クル開 発体制の実践
joooee0000
0
2.6k
美しいコードを書くためにF#を学んでみた話
yud0uhu
1
400
LLM/Agent評価:トップ営業の発言を「正解」にする 〜暗黙的正解による評価を営業資産に変える〜
takkuhiro
1
200
AIに「使われる」時代のSaaS戦略 〜既存WebAPIのMCPサーバー化における開発ノウハウ〜
ekispert_api
0
310
ヘルスケア領域における AI 活用と その安全性担保のための取り組み (Leveraging AI in Healthcare and Our Efforts to Ensure Its Safety) - Google I/O Extended Tokyo 2026, July 11, 2026
zettaittenani
0
260
AI時代の EM への処方箋
staka121
PRO
0
130
型は壁、Rustでもバグを直すな、表現できなくせよ
nwiizo
13
2k
Terraform共通モジュールをチーム横断で“変えられる”運用へ ― リリースと適用の分離
kekke_n
1
2.7k
脱金融のフューチャー・デザイン / Future Design Beyond Finance
ks91
PRO
0
150
知らん間に、回ってる
ming_ayami
0
400
ループエンジニアリングでE2Eテストを実践
noriyukitakei
0
340
AIと共生する開発者プラットフォーム:バクラクのモノレポ×マイクロサービス基盤
sakajunquality
2
3.4k
Featured
See All Featured
Between Models and Reality
mayunak
4
370
Writing Fast Ruby
sferik
630
63k
Bash Introduction
62gerente
615
220k
A better future with KSS
kneath
240
18k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
How to Talk to Developers About Accessibility
jct
2
320
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Believing is Seeing
oripsolob
1
170
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
260
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.3k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
410
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
67
56k
Transcript
Djangoテンプレートエンジンを 使いこなそう! DjangoCongressJP 2023 @tokibito
お前誰よ? • 岡野真也 • X (twitter): @tokibito • 株式会社ObotAI 取締役CTO
• 株式会社オープンコレクター 取締役 • Djangoフレームワークは2006年ごろから使ってる
アジェンダ • Djangoテンプレートエンジン概要 • アプリからの使い方 • テンプレート言語を使いこなす • テンプレートエンジンのカスタマイズ
Djangoテンプレートエンジン 概要
Djangoテンプレートエンジン • Djangoには、組み込みのテンプレートエンジンがあります。 • django.template
Djangoテンプレートエンジンの機能 • テンプレートを使って文字列を生成する • =レンダリングする • コンテキストを渡すことで、レンダリング時に動的に内容を変 更できる • テンプレート言語により、柔軟に出力内容を制御できる
• テンプレートローダーにより、テンプレートをファイルから読 み込める
テンプレートを使って 文字列を生成する
Django shellで試す >>> from django.template import engines >>> # テンプレートエンジンインスタンスの取得
>>> django_engine = engines["django"] >>> # テンプレート文字列をコンパイルして、Templateインスタンス生成 >>> template = django_engine.from_string("Hello {{ name }}!") >>> # コンテキストを指定してテンプレートをレンダリング >>> template.render({"name": "spam"}) 'Hello spam!' https://docs.djangoproject.com/en/4.2/topics/templates/#django.template.loader.engines
テンプレートにコンテキストを渡す • テンプレートにコンテキスト(context, 文脈)を渡すと、レンダ リング時に動的に内容を変更できる • キー(key)と値(value)の組み合わせで複数渡せる • Pythonの辞書を渡せる、と考えておけばOK
テンプレートのレンダリング テンプレート コンテキスト レンダリング結果 文字列 レンダリング
コンテキストを使う例 >>> from django.template import engines >>> # テンプレートエンジンインスタンスの取得 >>>
django_engine = engines["django"] >>> # テンプレート文字列をコンパイルして、Templateインスタンス生成 >>> template = django_engine.from_string("Hello {{ name }}!") >>> # コンテキストを指定してテンプレートをレンダリング >>> template.render({"name": "spam"}) 'Hello spam!' >>> # コンテキストを変更した場合 >>> template.render({"name": "egg"}) 'Hello egg!'
アプリからの使い方
Djangoドキュメントのチュートリアル • 「はじめての Django アプリ作成、その 3」 • https://docs.djangoproject.com/ja/4.2/intro/tutorial03/ • テンプレートファイルの作成、使い方の説明がある
ひとまずやってみる • プロジェクトのデフォルト設定で、テンプレートファイルを利用可能 • startappでmyappを作り、settings.pyのINSTALLED_APPSに追加してお く
from django.shortcuts import render # Create your views here. def
my_view(request): return render(request, 'spam.html', {'name': 'egg'}) myapp/views.py Hello {{ name }} myapp/templates/spam.html from myapp import views urlpatterns = [ path('', views.my_view), path('admin/', admin.site.urls), ] myproject/urls.py
None
コンテキストを使う例 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], #
任意のテンプレートディレクトリ追加 'APP_DIRS': True, # アプリのtemplatesを使うか 'OPTIONS': { 'context_processors': [ # コンテキストプロセッサ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
settings.py: BACKEND • テンプレートエンジンのバックエンド設定 • デフォルトはDjangoTemplates=Djangoテンプレートエンジン • 変更すると、Jinja2などのDjango以外のテンプレートエンジンを利 用可能 •
django.contrib.adminなど、Djangoに組み込まれている機能は、 Djangoテンプレートエンジンを前提にしている実装が多い • TEMPLATES設定はリストで複数記述できるので、Jinja2との併用 は可能 'BACKEND': 'django.template.backends.django.DjangoTemplates',
settings.py: DIRS • テンプレートファイルを配置するディレクトリ • 絶対パスで記述する必要がある。 'DIRS': [], # from
pathlib import Path # BASE_DIR = Path(__file__).resolve().parent.parent 'DIRS': [ BASE_DIR / 'templates', # プロジェクト直下のtemplatesディレクトリ ]
settings.py: APP_DIRS • アプリケーション内のtemplatesディレクトリを検索対象にす るかどうか • myapp/templates が検索対象となる。 'APP_DIRS': True,
settings.py: OPTIONS.context_processors • コンテキストプロセッサ、後ほど説明 'OPTIONS': { 'context_processors': [ # コンテキストプロセッサ
'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], },
View関数でテンプレートを使う from django.shortcuts import render # Create your views here.
def my_view(request): return render(request, 'spam.html', {'name': 'egg'}) myapp/views.py render関数は、便利なショートカット関数 render(request, template_name, context=None, content_type=None, status=None, using=None) https://docs.djangoproject.com/en/4.2/topics/http/shortcuts/#render
render関数の役割 • テンプレートファイルのロード • テンプレートのレンダリング • HttpResponseの作成
render関数を使わない場合 from django.http import HttpResponse from django.template import loader def
my_view(request): template = loader.get_template('spam.html') context = { 'name': 'egg', } return HttpResponse(template.render(context, request)) myapp/views.py
クラスベースビューでテンプレートを使う • django.views.generic.TemplateView • または、TemplateResponseMixinを継承したクラス
from django.views.generic import TemplateView class MyView(TemplateView): template_name = 'spam.html' def
get_context_data(self): context = super().get_context_data() context['name'] = 'egg' return context myapp/views.py from myapp import views urlpatterns = [ path('', views.MyView.as_view()), ] myproject/urls.py
テンプレート言語を使いこなす
テンプレート言語 • レンダリングの際に、テンプレート言語によって出力内容を変 更・制御できる • 変数: {{ 変数名 }} •
フィルター: {{ 変数名|フィルター:パラメータ }} • タグ: {% タグ名 パラメータ1 パラメータ2 ... %} • コメント: {# コメント #} https://docs.djangoproject.com/en/4.2/topics/templates/
テンプレート言語: 変数 • テンプレートに渡されたコンテキストを、テンプレート上で変 数として使用できる。 • {{ 変数名 }} >>>
template = django_engine.from_string("Hello {{ name }}!") >>> template.render({"name": "spam"}) 'Hello spam!'
変数: 辞書やリストへのアクセス def book_view(request): context = { 'book': { 'name':
'Django doc', 'pages': 200, 'tags': ['django', 'python'], }, } return render(request, 'book.html', context) myapp/views.py
name: {{ book.name }}<br> pages: {{ book.pages }}<br> tags: {{
book.tags }}<br> <br> {{ book.tags.0 }} {{ book.tags.1 }} myapp/templates/book.html • 辞書の場合、要素には “.キー名”でアクセスできる • リストの場合も、要素は “.index”でアクセスできる
None
テンプレート言語: フィルター >>> from datetime import datetime >>> template =
django_engine.from_string("Today is {{ value|date:'Y-m-d' }}") >>> template.render({"value": datetime(2023, 10, 7)}) 'Today is 2023-10-07' いろいろなフィルターがある https://docs.djangoproject.com/ja/4.2/ref/templates/builtins/#ref-templates-builtins-filters • 出力する前に、データを処理して、出力内容を変更できる • 変数に対してフィルターを設定する • {{ 変数名|フィルター:パラメータ }}
テンプレート言語: タグ >>> template = django_engine.from_string("Today is {% now 'Y-m-d'
%}") >>> template.render() 'Today is 2023-10-05' いろいろなタグがある https://docs.djangoproject.com/ja/4.2/ref/templates/builtins/#ref-templates-builtins-filters • フィルターと違って、独立した処理、機能を提供する。パラメータには文 字列や変数を指定できる。 • タグ: {% タグ名 パラメータ1 パラメータ2 ... %}
タグによる制御構文: if else endif • テンプレート内で条件分岐を利用できる。 >>> template = django_engine.from_string(
... "{% if value >= 5 %}{{ value }}は5以上{% else %}{{ value }}は5未満{% endif %}" ... ) >>> template.render({"value": 7}) '7は5以上' >>> template.render({"value": 3}) '3は5未満'
タグによる制御構文: for endfor • テンプレート内で繰り返し制御を利用できる。 >>> template = django_engine.from_string("{% for
x in data %}{{ x }}{% endfor %}") >>> template.render({"data": range(5)}) '01234'
タグによる構造化(1): include • 指定したテンプレートファイルを取り込む。 {% include "spam/egg.html" %}
タグによる構造化(2): extends block endblock • テンプレートの継承構造を作れる。 • 継承元のテンプレートでは、blockタグで変更可能なブロック を定義する •
継承したテンプレートでは、blockタグで変更したい部分だけ を定義する
Hello {% block name %}{% endblock %}! base.html {% extends
"base.html" %} {% block name %}bar{% endblock %} bar.html {% extends "base.html" %} {% block name %}foo{% endblock %} foo.html >>> from django.template import loader >>> template = loader.get_template("foo.html") >>> template.render() 'Hello foo!¥n' >>> template = loader.get_template("bar.html") >>> template.render() 'Hello bar!¥n'
Djangoテンプレートをどういうときに使う? • HTML生成のテンプレートとして • メール文章のテンプレートとして • テキスト形式のファイルを生成するテンプレートとして • ユーザーに表示するメッセージのテンプレートとして •
「文字列生成のときに使える」ということを覚えておくとよい
静的ファイルの取り扱い: staticタグ • staticタグを使うと、django.contrib.staticfilesで配信されるファイルのパスを生成できる {% load "static" %} <img src="{%
static "spam/egg.jpg" %}"> • 基本的に、静的ファイルのパスの指定はstaticタグを使う、と考えておけばよい。
リンクのパス生成: urlタグ urlpatterns = [ path("foo/bar/", views.MyView.as_view(), name="my_view"), ] urls.py
• urlタグを使うと、urls.pyで定義したViewのパスを生成できる <a href="{% url "my_view" %}">リンク</a> link.html
コンテキストプロセッサ • 汎用のコンテキストを供給する機能。 • レンダリング時の関数やメソッドにコンテキストを渡さなくて も、テンプレート内でコンテキストプロセッサのコンテキスト を利用できる
コンテキストプロセッサの設定 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], #
任意のテンプレートディレクトリ追加 'APP_DIRS': True, # アプリのtemplatesを使うか 'OPTIONS': { 'context_processors': [ # コンテキストプロセッサ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] settings.py
コンテキストプロセッサ: debug • 追加で使えるコンテキスト • debug: デバッグモードが有効かどうか • settings.DEBUG=Trueかつ、REMOTE_ADDRがINTERNAL_IPSに含まれている ものかどうか
• sql_queries: このテンプレートがレンダリングされるまでに実行され たSQLログ
コンテキストプロセッサ: request • 追加で使えるコンテキスト • request: Djangoのrequestオブジェクト(django.http.HttpRequest)
コンテキストプロセッサ: auth • django.contrib.authの機能。認証、権限。 • 追加で使えるコンテキスト • user: ログイン中のユーザー auth.Userまたは、未ログインの場合は
AnonymousUser • perms: 現在ログイン中のユーザーが有するパーミッション
コンテキストプロセッサ: messages • django.contrib.messages(メッセージフレームワーク)の機 能 • 追加で使えるコンテキスト • messages: メッセージフレームワークでセットされたメッセージ一覧。
• DEFAULT_MESSAGE_LEVELS: メッセージレベル名の数値
テンプレートエンジンの カスタマイズ
テンプレートエンジンのカスタマイズポ イント • 自作できるもの • フィルター • タグ • コンテキストプロセッサ
• テンプレートローダー • そもそもテンプレートエンジン自体を差し替えできる • これをやると何でもアリになるのが、ここでは解説しない
フィルターとタグをカスタマイズする準備 • アプリのディレクトリに templatetags という名前のディレクトリを作成 templatetags |--- __init__.py |--- my_customs.py
• 今回は my_customs という名前のモジュールに、カスタムのフィルターとタグを作成す る。 • テンプレート内で有効化するときは、 load タグでロードする。 {% load my_customs %} https://docs.djangoproject.com/ja/4.2/howto/custom-template-tags/
カスタマイズ: フィルター from django import template register = template.Library() @register.filter(name="cut")
def cut(value, arg): return value.replace(arg, "") my_customs.py https://docs.djangoproject.com/ja/4.2/howto/custom-template-tags/#writing-custom-template- filters
カスタマイズ: タグ from django import template register = template.Library() @register.simple_tag
def random_choice(): return value.replace(arg, "") my_customs.py https://docs.djangoproject.com/ja/4.2/howto/custom-template-tags/#writing-custom-template- filters
カスタマイズ: コンテキストプロセッサ • たくさんのページで使う可能性のあるコンテキストを追加した いときに利用 • 注意: コンテキストプロセッサは、テンプレートのレンダリン グ時に毎回実行されるので、遅い処理を入れると全体的に遅く なるかも
• 部分的に使いたいだけであれば、テンプレートタグで十分な場 合が多い
コンテキストプロセッサの作成 import sys def python_version(request): return {'python_version': sys.version} myapp/context_processors.py •
{{ python_version }} のように使える
コンテキストプロセッサの有効化 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS':
True, 'OPTIONS': { 'context_processors': [ ..., # 省略 'myapp.context_processors.python_version', ], }, }, ] settings.py
コンテキストプロセッサの変数を使って みる {{ python_version }} python_version.html
テンプレートローダーについて知る • Djangoテンプレートエンジンが、どこからテンプレートデータ をロードしてくるかを制御する • 組み込みのローダー • django.template.loaders.filesystem: ファイル •
django.template.loaders.app_directories: アプリのtemplatesディレクトリ • django.template.loaders.locmem: ローカルメモリ • django.template.loaders.cached: キャッシュ機能付きローダー https://docs.djangoproject.com/en/4.2/ref/templates/api/#loader-types
テンプレートローダーの設定 TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "OPTIONS": { "loaders":
[ ( "django.template.loaders.filesystem.Loader", [BASE_DIR / "templates"], ), ], }, } ] settings.py
カスタマイズ: テンプレートローダー • 例. データベースからテンプレートをロードする、テンプレー トローダーを自作してみる。 from django.db import models
class Template(models.Model): name = models.CharField(max_length=255) source = models.TextField() myapp/models.py
from django.template.loaders.base import Loader as BaseLoader from django.template import Origin
from .models import Template class DatabaseOrigin(Origin): def __init__(self, name, template_name=None, loader=None, object=None): super().__init__(name, template_name, loader) self.object = object class Loader(BaseLoader): def get_contents(self, origin): return origin.object.source def get_template_sources(self, template_name): template = Template.objects.filter(name=template_name).first() if not template: return [] origin = DatabaseOrigin( name=template_name, template_name=template_name, loader=self, object=template) return [origin] myapp/db_loader.py
テンプレートローダーの設定 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'OPTIONS': { 'context_processors':
[ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'loaders': [ (‘myapp.db_loader.Loader’,), # ここを追加 ( ‘django.template.loaders.app_directories.Loader’,), # Django adminなどが動くように ] } }, ] settings.py
None
None
まとめ • Djangoテンプレートエンジンは文字列生成に使う • テンプレートとコンテキストを組み合わせてレンダリングする • テンプレート言語で制御できる • 機能をカスタマイズできる
Django利用状況アンケート実施中! • Googleフォームから回答をお願いします。 • 回答受付期間:2023/10/2(月) ~ 2023/10/16(月) • https://forms.gle/ykQcgtHpWjpods348
ありがとうございました!