[Lightsail Django]No10 templatesを上書きする
デフォルトで用意されているtemplatesのカスタマイズ方法について整理して行きます。例えばdjango-allauthをインストールするとデフォルトで用意されている画面が使用されますが、所定のディレクトリに配置するのみで、独自のtemplates(html)を使用することができます。
1.テンプレートの差し替えについて
1-1.テンプレートを差し替えるには、templatesフォルダ以下で同階層、同ファイル名にする
モジュールのテンプレートを書き換える際はtemplatesフォルダ以下で同じ階層、同ファイル名にすると上書きができます。
#/opt/bitnami/projects/[プロジェクト名]/templates
/opt/bitnami/projects/TEST/templates
1-2.上書きたいtemplateの階層やファイル名については、git-hubを参考にする
例えばdjango-allauthの階層やファイル名については、django-allauthのgit-hubを参考にすると良いかと思います。
django-allauth/allauth/templates/account配下に、login.html、logout.htmlなどがありますので、プロジェクトフォルダ直下のtemplatesフォルダ内に「account」フォルダを作り、login.htmlを配置すると、django-allauthのログイン画面表示時に新たに配置したlogin.htmlの内容で表示されます。
#/opt/bitnami/projects/[プロジェクト名]/templates/account/login.html
/opt/bitnami/projects/TEST/templates/account/login.html
ここでいうログイン画面は、前回の説明で出てきた以下URLのことを指します。
http://[IPアドレス]/account/login/
前回の記事はこちら
1-3.カスタマイズするhtmlは専用のタグや変数の配置に注意
htmlをカスタマイズする場合は、デフォルトのtemplateで使用されていたタグや変数を必要に応じて配置する必要があります。
タグや変数を配置しないと必要なパラメータが欠損し正しい挙動とならない場合や、必要な表示項目が表示されないなどの不具合が発生する可能性があるためです。
逆にhtmlはほとんど修正せずにCSSで画面レイアウトを変更するやり方もあります。
1-4.タグや変数を精査する必要があるので、カスタマイズするのは大変そう
いずれにせよ、オリジナルのtemplateで使用されていたタグや変数については、多少精査するのがカスタマイズする前提となるかと思いますので、若干大変そうな気もしますね、、
ただタグや変数の精査ができて、正しいhtmlが用意できれば、1からロジックを作るよりかは実装やテストの労力は減るはずなので良さそうな気もするので悩ましいところです。(カスタマイズせずに微妙なデザインで使うのが一番労力が掛からなそうですが、なんか画面レイアウトはなんとかしたくなりますよね)
おまけ(django-allauthのテンプレートカスタマイズについて色々模索)
前回django-allauthの設定し、認証機能が使用できるようになりましたが、デフォルトで用意されているログイン画面やサインアップ画面はかなり微妙でしたので、templatesをカスタマイズしたく、色々模索してみました。
django-allauthのtemplates変更手順の説明でなく、作業メモ的なものですので、おまけとしています。。
2.templateについて少しおさらい
2-1.htmlで使用するタグについて
{{ }}は、変数。ビューから渡されたcontextデータを表示。
{% %} は、タグ。
2-2.loadタグ
loadタグは、指定されたpyモジュールを読み込むようなイメージで、以下例でいうと、allauth.py、account.pyが読み込み対象で、かつモジュール内で、register = template.Library()が実施されている必要があります。
allauth/templates/account/login.html
{% load allauth account %}
allauth/templatetags/allauth.py
#...
register = template.Library()
#...
@register.tag(name="element")
def do_element(parser, token):
nodelist = parser.parse(("endelement",))
tag_name, args, kwargs = parse_tag(token, parser)
usage = f'{{% {tag_name} "element" argument=value %}} ... {{% end{tag_name} %}}'
if len(args) > 1:
raise template.TemplateSyntaxError("Usage: %s" % usage)
parser.delete_first_token()
return ElementNode(nodelist, args[0], kwargs)
以下elementタグは、上記elementメソッドがコールされるイメージです。
parser.parse(("endelement",))は、endelementまで読み進め、
django.template.NodeList のインスタンスを返します。
{% endelement %}がまだ残ってしまっているので、 parser.delete_first_token()を実施し削除します。
ElementNodeのロジックを見るとh1用のテンプレート(allauth/templates/allauth/elements/h1.html)を読み込むようですが、解読がきつくなってきたので、実際にhtml展開後の内容を見ることに。
allauth/templates/account/login.html
{% element h1 %}
{% trans "Sign In" %}
{% endelement %}
実際に展開されたhtmlイメージ
<!-- element h1 -->
<h1>
ログイン
</h1>
<!-- end element h1 -->
loadタグでは、accountもロードされていましたが、allauth/account/templatetags/account.pyは以下。
#...
register = template.Library()
@register.simple_tag(name="user_display")
def user_display_tag(user):
"""
Example usage::
{% user_display user %}
or if you need to use in a {% blocktrans %}::
{% user_display user as user_display %}
{% blocktrans %}
{{ user_display }} has sent you a gift.
{% endblocktrans %}
"""
return user_display(user)
3.django-allauthのテンプレートを参考に必要なタグを整理する
独自のhtmlに差し替えていく際にdjango-allauthタグやパラメータが必要になってきますので、django-allauthテンプレートのallauth/templates/account/login.htmlを参考に必要なタグやパラメータ名などを整理して行きます。
3-1.URLの変数
以下ではsignup_urlとありますが、URLの変数は、allauth/account/urls.py辺りに定義されており、URLを使用する際にはallauth/account/urls.pyにname定義されたものを使用するで良さそう。
allauth/templates/account/login.html
<a href="{{ signup_url }}">sign up</a>
実際に展開されたhtml
<a href="/account/signup/">
allauth/account/urls.py(以下を使用するで良さそう)
from django.urls import path, re_path
from . import views
urlpatterns = [
path("signup/", views.signup, name="account_signup"),
path("login/", views.login, name="account_login"),
path("logout/", views.logout, name="account_logout"),
path("reauthenticate/", views.reauthenticate, name="account_reauthenticate"),
path(
"password/change/",
views.password_change,
name="account_change_password",
),
path("password/set/", views.password_set, name="account_set_password"),
path("inactive/", views.account_inactive, name="account_inactive"),
# Email
path("email/", views.email, name="account_email"),
path(
"confirm-email/",
views.email_verification_sent,
name="account_email_verification_sent",
),
re_path(
r"^confirm-email/(?P<key>[-:\w]+)/$",
views.confirm_email,
name="account_confirm_email",
),
# password reset
path("password/reset/", views.password_reset, name="account_reset_password"),
path(
"password/reset/done/",
views.password_reset_done,
name="account_reset_password_done",
),
re_path(
r"^password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$",
views.password_reset_from_key,
name="account_reset_password_from_key",
),
path(
"password/reset/key/done/",
views.password_reset_from_key_done,
name="account_reset_password_from_key_done",
),
]
例えば、htmlに以下のように設定すると、
{% url 'account_login' %}
以下のように展開される
/account/login/
3-2.formの開始タグ
フォームタグについては、単純にpostの指定と、actionの指定のみですので、このタグは使用せずに、普通のformタグにURL用の変数を設定する形で良いかと思います。
allauth/templates/account/login.html
{% element form form=form method="post" action=login_url tags="entrance,login" %}
実際に展開されたhtml
<form method="post" action="/account/login/">
3-3.CSRFトークン
CSRFトークンはDjangoの機能で、前回、settings.pyに記載した'django.middleware.csrf.CsrfViewMiddleware'によってCSRF検証機能が設定されます。
{% csrf_token %}をそのまま使用して行きます。
allauth/templates/account/login.html
{% csrf_token %}
実際に展開されたhtml
<input type="hidden" name="csrfmiddlewaretoken" value="xxxxxxxxxxxx">
3-4.ログインのサブミットボタン
単純なボタンタグのサブミットなので、こちらのタグは使用しなくても良さそうです。
allauth/templates/account/login.html
{% element button type="submit" tags="prominent,login" %}
{% trans "Sign In" %}
{% endelement %}
実際に展開されたhtml
<button
type="submit"
>
ログイン
</button>
3-5.フォームのパラメータ項目
フォームのパラメータ名については、allauthに合わせたものを使用する必要があるので、パラメータ名について整理して行きます。
allauth/templates/account/login.html
{% element fields form=form unlabeled=True %}{% endelement %}
実際に展開されたhtml
<p>
<label for="id_login">メールアドレス:</label>
<input type="email" name="login" placeholder="メールアドレス" autocomplete="email" maxlength="320" required id="id_login">
</p>
<p>
<label for="id_password">パスワード:</label>
<input type="password" name="password" placeholder="パスワード" autocomplete="current-password" required id="id_password">
<span class="helptext"><a href="/account/password/reset/">パスワードをお忘れですか?</a></span>
</p>
<p>
<label for="id_remember">ログインしたままにする:</label>
<input type="checkbox" name="remember" id="id_remember">
</p>
整理すると、パラメータ名は以下のように用意する必要があります。
メールアドレス:name="login"
パスワード:name="password"
ログインしたままにする:name="remember"
ただし入力エラー時には以下エラーメッセージも展開される仕様なので、この{% element fields form=form unlabeled=True %}タグは必ず使用しないとダメそうですね、、
このタグを使用すると入力フォームに使用するinputタグ一式がまるッと展開されてしまうので、inputタグレベルではカスタマイズができなそうですし、、
<ul class="errorlist nonfield">
<li>入力されたメールアドレスもしくはパスワードが正しくありません。</li>
</ul>
<p>
<label for="id_login">メールアドレス:</label>
<input type="email" name="login" placeholder="メールアドレス" autocomplete="email" maxlength="320" required id="id_login">
</p>
<p>
<label for="id_password">パスワード:</label>
<input type="password" name="password" placeholder="パスワード" autocomplete="current-password" required id="id_password">
<span class="helptext"><a href="/account/password/reset/">パスワードをお忘れですか?</a></span>
</p>
<p>
<label for="id_remember">ログインしたままにする:</label>
<input type="checkbox" name="remember" id="id_remember">
</p>
4.結論
現状整理した中でいうと、allauthのタグや変数を独自templatesに埋め込んでいくだけでは、画面レイアウトを変更できなそうです。
(入力エラーメッセージを表示するタグが見つからなかったため)
htmlタグを自由にカスタマイズでき、必要なタグや変数のみをそのhtmlに配置すれば使い勝手が良さそうと思ったのですが、残念〜。。
やはりCSSでの書き換え作戦しかないのか??
気が向いた時にもうちょっと精査してみようかと思います。
この記事が気に入ったらサポートをしてみませんか?