Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Djangoテンプレートエンジンを使いこなそう!

Shinya Okano
October 06, 2023

 Djangoテンプレートエンジンを使いこなそう!

Shinya Okano

October 06, 2023
Tweet

More Decks by Shinya Okano

Other Decks in Technology

Transcript

  1. お前誰よ? • 岡野真也 • X (twitter): @tokibito • 株式会社ObotAI 取締役CTO

    • 株式会社オープンコレクター 取締役 • Djangoフレームワークは2006年ごろから使ってる
  2. 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
  3. コンテキストを使う例 >>> 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!'
  4. 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
  5. コンテキストを使う例 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', ], }, }, ]
  6. settings.py: BACKEND • テンプレートエンジンのバックエンド設定 • デフォルトはDjangoTemplates=Djangoテンプレートエンジン • 変更すると、Jinja2などのDjango以外のテンプレートエンジンを利 用可能 •

    django.contrib.adminなど、Djangoに組み込まれている機能は、 Djangoテンプレートエンジンを前提にしている実装が多い • TEMPLATES設定はリストで複数記述できるので、Jinja2との併用 は可能 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  7. settings.py: DIRS • テンプレートファイルを配置するディレクトリ • 絶対パスで記述する必要がある。 'DIRS': [], # from

    pathlib import Path # BASE_DIR = Path(__file__).resolve().parent.parent 'DIRS': [ BASE_DIR / 'templates', # プロジェクト直下のtemplatesディレクトリ ]
  8. 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', ], },
  9. 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
  10. 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
  11. 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
  12. テンプレート言語 • レンダリングの際に、テンプレート言語によって出力内容を変 更・制御できる • 変数: {{ 変数名 }} •

    フィルター: {{ 変数名|フィルター:パラメータ }} • タグ: {% タグ名 パラメータ1 パラメータ2 ... %} • コメント: {# コメント #} https://docs.djangoproject.com/en/4.2/topics/templates/
  13. 変数: 辞書やリストへのアクセス def book_view(request): context = { 'book': { 'name':

    'Django doc', 'pages': 200, 'tags': ['django', 'python'], }, } return render(request, 'book.html', context) myapp/views.py
  14. name: {{ book.name }}<br> pages: {{ book.pages }}<br> tags: {{

    book.tags }}<br> <br> {{ book.tags.0 }} {{ book.tags.1 }} myapp/templates/book.html • 辞書の場合、要素には “.キー名”でアクセスできる • リストの場合も、要素は “.index”でアクセスできる
  15. テンプレート言語: フィルター >>> 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 • 出力する前に、データを処理して、出力内容を変更できる • 変数に対してフィルターを設定する • {{ 変数名|フィルター:パラメータ }}
  16. テンプレート言語: タグ >>> 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 ... %}
  17. タグによる制御構文: 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未満'
  18. 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'
  19. 静的ファイルの取り扱い: staticタグ • staticタグを使うと、django.contrib.staticfilesで配信されるファイルのパスを生成できる {% load "static" %} <img src="{%

    static "spam/egg.jpg" %}"> • 基本的に、静的ファイルのパスの指定はstaticタグを使う、と考えておけばよい。
  20. リンクのパス生成: 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
  21. コンテキストプロセッサの設定 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
  22. テンプレートエンジンのカスタマイズポ イント • 自作できるもの • フィルター • タグ • コンテキストプロセッサ

    • テンプレートローダー • そもそもテンプレートエンジン自体を差し替えできる • これをやると何でもアリになるのが、ここでは解説しない
  23. フィルターとタグをカスタマイズする準備 • アプリのディレクトリに templatetags という名前のディレクトリを作成 templatetags |--- __init__.py |--- my_customs.py

    • 今回は my_customs という名前のモジュールに、カスタムのフィルターとタグを作成す る。 • テンプレート内で有効化するときは、 load タグでロードする。 {% load my_customs %} https://docs.djangoproject.com/ja/4.2/howto/custom-template-tags/
  24. カスタマイズ: フィルター 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
  25. カスタマイズ: タグ 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
  26. コンテキストプロセッサの有効化 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS':

    True, 'OPTIONS': { 'context_processors': [ ..., # 省略 'myapp.context_processors.python_version', ], }, }, ] settings.py
  27. テンプレートローダーについて知る • 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
  28. テンプレートローダーの設定 TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "OPTIONS": { "loaders":

    [ ( "django.template.loaders.filesystem.Loader", [BASE_DIR / "templates"], ), ], }, } ] settings.py
  29. 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
  30. テンプレートローダーの設定 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