Django 動的にhtmlにヘッダタグを付ける

Djangoのページにカスタムヘッダ(タグ)を動的に挿入する。

<head>タグ中に独自のヘッダ(タグ)を動的に挿入する手段が欲しかった。 単純にモデルで管理してテンプレート内で展開すればいいことに気づいたのでメモを残しておく。

基本方針

  • ヘッダは複数でも可
  • ヘッダは追加後の修正も簡単にできる
  • ヘッダはadminサイトから操作可能(ソースをいじる必要はなし)

まあ普通のような気もする。adminサイトから操作できれば上の2つは達成できるようなものだから、adminサイトで操作さえできれば、ほぼできたようなものだ。残る問題はhtmlでどう読み込めばいいかくらいになる。

こういう感じにする

テンプレートのレンダリングのときに、以下のadminサイトから変更できるタグを挿入可能にする。

<html>
<head>
    <!-- テンプレートで書いてあるヘッダタグ -->

    <!-- adminサイトから変更できるタグ -->
    <meta name="サンプル" content="metaタグ以外も可能">
</head>
<body>
    <!-- ページの中身 -->
</body>
</html>

やること

ここからは実装。と言ってもそんなにすることはない。過去の記事を活かせば難しくない。

  • カスタムヘッダをモデルとして定義
  • adminサイトで操作可能にする
  • htmlレンダリング時、コンテキストにカスタムヘッダを挿入する
  • テンプレートでカスタムヘッダをレンダリングするようにする

これらを実装すればいい。順に説明する。

実装

カスタムヘッダをモデルとして定義

まずは<app>/models.pyにカスタムヘッダを定義する。

from django.db import models

class CustomHeader(models.Model):
    name = models.CharField(max_length=40)
    tags_text = models.TextField(
        help_text='Some tags text(html format) in html > head')

    def __str__(self):
        return self.name

CustomHeaderは、nametags_textの2つのフィールドを持つ。

nameはカスタムヘッダの識別用の名前であり、これ自体はレンダリングはしない。 tags_textはレンダリングする内容。エスケープを考慮する必要はなく、<>などもそのまま含めていい。

adminサイトで操作可能にする

admin.pyに記述しないとadminサイトには現れないので、忘れず記述する。 ここのあたりはDjangoのチュートリアルのpollアプリケーションにもあったと思う。

from django.contrib import admin

# <app>/models.pyをインポート
from . import models

# Register your models here.
admin.site.register(models.CustomHeader)
...

これだけ。あとは…マイグレーションも必要か。python manage.py makemigrationsでよかったと思う。

これで/admin/にアクセスしてログインすれば、CustomHeaderモデルが見える。

コンテキストにカスタムヘッダを挿入する

以前書いた記事で使ったコンテキストプロセッサを使う:

Django コンテキストプロセッサでテンプレートコンテキストに共通の変数を埋め込む
Djangoの全てのhtmlテンプレートで共通の変数を埋め込む。 テンプレートコンテキスト(以下コンテキスト)に、変数名をキーとして、値を値として埋め込めばいい。 方法はとりあえず3通りある。 1つ1つのビューに書く ビューが少なければあま...

実装したコンテキストプロセッサは次のようなもの:

from django.http import HttpRequest
from app.models import CustomHeader

def add_head_tags(request: HttpRequest):
    """ カスタムヘッダを挿入するためのコンテキストを付加するコンテキストプロセッサ """
    head_tags = [i.tags_text for i in CustomHeader.objects.all()]
    return {'head_tags': head_tags}

やっていることはとてもシンプル。コンテキストのhead_tagsCustomHeaderモデルの全てのオブジェクトのtags_textを格納するだけ。特定のものだけにしたいのならば、ここでフィルタリングなどを行なっておけばいいだろう。

そして、Djangoの設定ファイルで、TEMPLATESOPTIONScontext_processorsへこのコンテキストプロセッサを追加するのを忘れないようにする。

テンプレートでカスタムヘッダをレンダリングする

ここまできたらテンプレートでレンダリングするだけだ。このappでは、全てのページは同じベーステンプレートを使用しているので、そのベーステンプレートファイルの<head>部分を変更すればいい。

...
<head>
  ...
  {% autoescape off %}
  {% for head_tag in head_tags %}
  {{ head_tag }}
  {% endfor %}
  {% endautoescape %}
</head>
...

エスケープしないようにして、タグを展開するだけ。 Djangoのテンプレートの自動エスケープについては、以前書いた: Django Template Languageでの変数のエスケープ回避 | ikapblog

これで実装は終わり。

動作確認

adminサイトでデータを追加するのが楽だろう。adminサイトを使いたくないときは、pythonを起動して直接操作するのだと思う。

作用のないタグを適当に追加してみて、ページを開いてソースを確認すればヘッダ(タグ)が追加されているのがわかるはず。

そのほかの実装方法

他の実装方法も多々あると思うので思いつく方法をメモしておく。モデルとして管理しておくのはソースを変更しないで単独で実現する上でほぼ必須となるので、モデルの使用は前提としておく。 なので、他の実装方法といっても、レンダリングの他の方法くらいの意味になる。

上記方法に対してのメリット・デメリットを書いておく。

コンテキストを使わずに、カスタムテンプレートタグを利用する

コンテキストに格納するのでなく、カスタムタグの呼び出し時にオブジェクトを呼び出す方法。

  • メリット
    • コンテキストからカスタムヘッダを取り出すためのキーを意識する必要がなくなる
    • カスタムタグ({% add_headers %}など)を書いておけばいい
    • コンテキストプロセッサが不要になる
  • デメリット
    • カスタムタグを用意する必要がある

こちらの方が簡単かも…。コンテキストに格納するキーを一致させる必要がないのは大きなメリットだと思う。

おわりに

アクセス解析などでは、ヘッダにタグを設置するよう求められることが多いのでDjangoでそういうことをしたいのであれば、上のような実装は必要になる。

この実装なら後からの修正や追加・削除が簡単に済むので管理が簡単になる。他の方法もあるのかもしれないが、その辺りの調査はまた今度。

以上です。


Django関連書籍(広告)

https://amzn.to/36bB6Hi
タイトルとURLをコピーしました