diadia

興味があることをやってみる。自分のメモを残しておきます。

django-allauth : テンプレートのカスタマイズ

django-allauthを使ってみた結果ユーザのログインページが味気ない感じだった。

ここを修正していこうと思う。

 

https://qiita.com/s-katsumata/items/b667c81a127223d2e868

こちらにテンプレートのカスタマイズ方法があった。これを参考にテンプレートをカスタマイズしてみようと思う。

django-allauthのテンプレートのコピー

コマンドを使ってファイルを操作することに苦手なのでここもメモしておく。
まずコピーのコマンドは以下のような形式である。

 

copy コピー元ファイル コピー先のディレクト

つぎにコピー元のファイルがどこに有るか?
django-allauthはpip でインストールした。pip でインストールした場合には、pip show モジュール名で情報参照できるようだ。pipでインストールしたパッケージの場所を調べる

pip show django-allauth
Name: django-allauth
Version: 0.38.0
Summary: Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
Home-page: http://github.com/pennersr/django-allauth
Author: Raymond Penners
Author-email: raymond.penners@intenct.nl
License: UNKNOWN
Location: /anaconda3/envs/django36/lib/python3.6/site-packages
Requires: python3-openid, requests-oauthlib, requests, Django
Required-by: 

locationがdjango-allauthのモジュールだ。ここからaccount,openid,socialaccount,base.htmlをディレクトリごとにコピーしておく。コピー先はテンプレートの優先順位を上げたい思惑からBASE_DIR/config/templates/allauth以下にコピーする。するとallauth以下にaccount,openid,socialaccountディレクトリが置かれる状態となる。
テンプレートはドキュメントで以下のように言及されている。

allauth ships many templates, viewable in the allauth/templates directory.

https://django-allauth.readthedocs.io/en/latest/templates.html#overridable-templates 

settings.pyのテンプレートの読み込み先を追加する

settings.pyのDIRSの欄にallauth部分を追加する。
djangoのテンプレートの読み込みは優先順序がある。それはtemplateのDIRSをまず読み込み、該当するテンプレートがなかった場合に各アプリのtemplatesディレクトリ(つまりAPP_DIRS)以下を読み込みに行く。そして言うまでもないが、各アプリはsettings.pyのINSTALLED_APPSに登録されて使われることが前提である。allauthの場合も同様、INSTALLED_APPSにallauthを登録するして初めてつかわれる。このことはテンプレートにおいてはallauthアプリ以下にあるテンプレートを読み込んでいるに過ぎない。したがってDIRSにカスタマイズしたallauthテンプレートのパスを記せば、カスタマイズのテンプレートを純正のテンプレートを無視して自己カスタマイズしたテンプレートを読みこむことになる。
https://docs.djangoproject.com/ja/2.2/topics/templates/#support-for-template-engines

具体的なコード例
config/settings.py にて
    
TEMPLATES = [
    {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, "config", "templates"),
            os.path.join(BASE_DIR, "config", 'templates', 'allauth')],  # ←ココ
    'APP_DIRS': True,
    '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',
            ],
        },
    },
]

#  上記の追加はBASE_DIR/config/templates/allauthのテンプレ読込先を追加したことを意味する!

上記の追加がなければ、BASE_DIR/config/templates以下のテンプレートをまず探して、該当テンプレートがなければAPP_DIRSを探す。そしてallauth純正のテンプレートを探し当てる。上記を追加すればBASE_DIR/config/templates以下のテンプレートをまず探す。なければ、BASE_DIR/config/templates/allauth以下にテンプレートがないか探す。ここにカスタマイズしたテンプレートを置けば純正allauthテンプレートを探す前に見つけてもらえるのでカスタマイズテンプレートが実際使われる。



テンプレートのカスタマイズ

各テンプレートはextends base.htmlとされているので、それに対しbootstrapのコードを加えていく。

forms.pyのカスタマイズ

forms.pyのフィールドにclassのアトリビュートを足してbootstrap効果をつけたいと考える。この場合はどうするか。。。?

https://django-allauth.readthedocs.io/en/latest/forms.html#forms

https://www.reddit.com/r/django/comments/3uykx8/customizing_djangoall_auth/

 

結論から言うと、settings.pyにカスタムしたフォームを使う宣言と、allauthのloginformを継承した子フォームさえ作成すれば良い。

 

settings.pyにカスタムしたフォームを使う宣言

ACCOUNT_FORMS = {'login': 'project.forms.CustomLoginForm'}
上記のように宣言すると、settings.pyにてallauth純正ではなく自分がカスタムしたフォームを使う挙動に変更される。

https://django-allauth.readthedocs.io/en/latest/forms.html#account-forms

デフォルトでは以下のような設定になっているカスタムする場合は下記を参考にkeyをsettings.pyに書き込む。。

ACCOUNT_FORMS = {
    'login': 'allauth.account.forms.LoginForm',
    'signup': 'allauth.account.forms.SignupForm',
    'add_email': 'allauth.account.forms.AddEmailForm',
    'change_password': 'allauth.account.forms.ChangePasswordForm',
    'set_password': 'allauth.account.forms.SetPasswordForm',
    'reset_password': 'allauth.account.forms.ResetPasswordForm',
    'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm',
    'disconnect': `allauth.socialaccount.forms.DisconnectForm`,
}

allauthのloginformを継承した子フォームの作成

from allauth.account.forms import LoginForm


class CustomLoginForm(LoginForm):

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		for field in self.fields.values():
			field.widget.attrs['class'] = "form-control"