Djangoフレームワークのテンプレートエンジンは、『テンプレートをロードする処理』を自分で実装した処理に差し替えるための設定が用意されています。
django.template.loaders.base.Loader
を継承したクラスを実装し、 settings.py
で TEMPLATES
を設定すると、処理を差し替えることができます。
※この記事の内容は、DjangoCongressJP 2023で発表した内容の一部をまとめなおしたものになります。
Djangoテンプレートエンジンを使いこなそう! - DjangoCongressJP 2023 @tokibito
アプリの用意
まずは、変更の比較のためにテンプレートを単純にレンダリングするビューを含むアプリを用意します。
python manage.py startapp myapp
myappのアプリをstartappで追加したら、 settings.py
の INSTALLED_APPS
に追加して、有効にしておきます。
myapp/views.py
from django.shortcuts import render def my_view(request): return render(request, 'spam.html', {'name': 'egg'})
myapp/templates/spam.html
Hello {{ name }}
myproject/urls.py
from django.contrib import admin from django.urls import path from myapp import views urlpatterns = [ path('', views.my_view), path('admin/', admin.site.urls), ]
デフォルトのテンプレートローダーの動作を確認
この状態でrunserverを起動し、 http://127.0.0.1:8000/ にアクセスすると、
myapp/templates/spam.html` が使われる表示となります。
テンプレートローダーを自作する
今回は、データベースからテンプレートデータをロードするようなローダーを作成してみます。
models.py
テンプレートデータを保存しておくモデルです。 name
にテンプレート名(テンプレートパス)を入れておき、 source
にテンプレートの内容を入れておく想定です。
from django.db import models class Template(models.Model): name = models.CharField(max_length=255) source = models.TextField() def __str__(self): return f"{self.name}"
makemigrationsとmigrateでテーブルを作っておきます。
python manage.py makemigrations myapp python manage.py migrate myapp
myapp/admin.py
Django adminからTemplateのモデルを編集できるように、admin.pyを書き換えます。
from django.contrib import admin from .models import Template admin.site.register(Template)
myapp/db_loader.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クラスでラップして返す。 origin = DatabaseOrigin( name=template_name, template_name=template_name, loader=self, object=template) return [origin]
これでテンプレートローダーは完成です。
自作したテンプレートローダーを有効にする
作成したテンプレートローダーを有効にしてみます。 settings.py
の TEMPLATES
設定を書き換えます。
APP_DIRS
を削除(またはコメントアウト)して、 loaders
キーを追加し、作成したテンプレートローダーと app_directories.Loader
を設定します。
settings.py(抜粋)
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 'APP_DIRS': True, # APP_DIRSは、loaders設定を入れる場合には、設定できない。 '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 adminなどが動作するように、app_directories.Loaderを有効にしておく 'django.template.loaders.app_directories.Loader', ), ] } }, ]
動かしてみる
この状態で http://127.0.0.1:8000
にアクセスすると、データベースにはテンプレートデータがまだ無いので、ファイルから読み込まれたテンプレートが使用され、挙動に変化はありません。
Django adminからnameを spam.html
としたテンプレートデータを追加します。
そうすると、今度は自作したテンプレートローダー側が読み取った、データベース上のデータがテンプレートとして使用されます。
データベース上のテンプレートデータを利用するテンプレートローダーを作成することができました。