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

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

Avatar for Shinya Okano Shinya Okano
October 06, 2023

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

Avatar for Shinya Okano

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