【Django】django-allauthのformやhtmlを上書きする方法

## 1, allauthとは
viewを自分で書かなくても次の様なことが簡単にできるようになる。

* signup
* login
* passwordのリセット・変更
* mail addressの登録・変更・削除
* social login

## 2, インスール
このページでは、formsやhtmlの上書きについて書くので基本的な設定については割愛します。

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

```terminal:terminal
$ pip install django-allauth
```

## 3, formsの上書き
### settings.py
まず下準備をしておく。

allauthの使いたいフォームをリストアップし、settings.pyに上書きする指示を出します。

social accountを使う場合はほぼ全て必要になります。

#### キーとデフォルト値
まずデフォルトの設定を見てみます。

`allauth.account.forms.`以降を調整すれば上書きすることができます。

|キー|デフォルト値
|---|---|
signup|allauth.account.forms.SignupForm
login|allauth.account.forms.LoginForm
reset_password|allauth.account.forms.ResetPasswordForm
reset_password_from_key|allauth.account.forms.ResetPasswordKeyForm
change_password|allauth.account.forms.ChangePasswordForm
add_email|allauth.account.forms.AddEmailForm
set_password|allauth.account.forms.SetPasswordForm

簡単な説明

|キー|説明|
|---|---|
signup|signupページのフォーム
login|loginページのフォーム
reset_password|passwordリセットページのメアド入力フォーム
reset_password_from_key|リセットメールから遷移した新password入力フォーム
change_password|password変更ページのpassword入力フォーム
add_email|emailの設定・追加・削除ページの入力フォーム
set_password|password設定ページの入力フォーム（social用）


### 設定例
`allauth.account.forms.`以降の名前を変更します。

好きな名前で良いですが、分かりやすく統一しましょう。

ここでは、SignupFormの前に`Custom`を付けて`CustomSignupForm`としていますが、`My`を付けて`MySignupForm`などでもOKです。

書く場所はsettings.py下の方であればどこでも大丈夫です！

```python:settings.py
#allauth formの割り込み
ACCOUNT_FORMS = {
    'signup': 'accounts.forms.CustomSignupForm',
    'login': 'accounts.forms.CustomLoginForm',
    'reset_password': 'accounts.forms.CustomResetPasswordForm',
    'reset_password_from_key': 'accounts.forms.CustomResetPasswordKeyForm',
    'change_password': 'accounts.forms.CustomChangePasswordForm',
    'add_email': 'accounts.forms.CustomAddEmailForm',
    'set_password': 'accounts.forms.CustomSetPasswordForm',
}
```

これが完了したら、次は`forms.py`に`__init__`を上書きします。

### forms.py
allauthのFormをimportし、`__init__`に上書きする内容を書きます。

```python:forms.py
from allauth.account.forms import (
    SignupForm,
    LoginForm,
    ResetPasswordForm,
    ResetPasswordKeyForm,
    ChangePasswordForm,
    AddEmailForm,
    SetPasswordForm, 
)

#クラス名はsettings.pyで書いた名前
class CustomSignupForm(SignupForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        #classにbootstrapのform-controlを指定したい場合
        self.fields['email'].widget.attrs['class'] = 'form-control'
        self.fields['password1'].widget.attrs['class'] = 'form-control'
        self.fields['password2'].widget.attrs['class'] = 'form-control'
        #placeholderを設定したい場合
        self.fields['email'].widget.attrs['placeholder'] = '任意の文字'
        self.fields['password1'].widget.attrs['placeholder'] = '任意の文字'
        self.fields['password2'].widget.attrs['placeholder'] = '任意の文字'
        #labelを設定したい場合
        self.fields['email'].label = '任意の文字'
        self.fields['password1'].label = '任意の文字'
        self.fields['password2'].label = '任意の文字'
```

実際は下記のような感じになるかと思います。

```python:forms.py
class CustomSignupForm(SignupForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
        #placeholderが不要なら
        for field in self.fields.values():
            field.widget.attrs['placeholder'] = ''
        #labelが不要なら
        for field in self.fields.values():
            field.label = ''
```

`form-control`だけ設定する前提で全て書いていきます。

```python:forms.py
#allauthのsignupフォーム上書き
class CustomSignupForm(SignupForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのログインフォーム上書き
class CustomLoginForm(LoginForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのパスワードリセットのメールフォーム上書き
class CustomResetPasswordForm(ResetPasswordForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのnewパスワードフォーム上書き
class CustomResetPasswordKeyForm(ResetPasswordKeyForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのパスワード変更フォーム上書き
class CustomChangePasswordForm(ChangePasswordForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのメール設定フォーム上書き
class CustomAddEmailForm(AddEmailForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

#allauthのパスワード設定フォーム
class CustomSetPasswordForm(SetPasswordForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
```

これで、デフォルトのフォームからBootstrapの`form-contro`を適応したフォームに変えることができました。

しかーし！フォームが良くなっても見た目が・・・となるので次はhtmlを上書きしちゃいましょう！

## 4, htmlの上書き
直接allauthのファイルに書き込むわけにはいかないので、allauthのフォルダからコピーを持ってきます。

### ファイルの場所
これは人によってフォルダ名が変わりますが、

`env >  lib > site-packages`の中に`allauth`があります。

```html:ファイルの場所
allauth
    templates （この中にhtml等のファイルがあります）
        account
            email（emailのフォルダ）
                hoge.txt
            hoge.html
            hoge2.html
        socialaccount
　　　　　　　　　　　　　　　　　　　　　　　　snippets（snippetsのフォルダ)
                provider_list.html
            hoge.html
            hoge2.html
```

構造を同じにするために、<b>自身のtemplatesフォルダ</b>に`account`とさらにその中に`email`フォルダを作ります。

socialloginも実装するのであれば、templatesに`socialaccount`フォルダとその中に`snippets`フォルダも作ります。

フォルダが作れたら、allauthのフォルダから必要なhtmlファイルをコピーして持ってきましょう！

まぁこれが大変で、どのページがどのファイルなのかわからない！ｗ

なので、私が上書きしているhtmlの一覧を載せておきます！

自分のフォルダにallauthからコピペして、仮想環境でいじりながらチェックするのが早いと思います。

### account / email / file

|ファイル名|ページ内容|
|---|---|
email_confirmation_message.txt|メアド確認完了のメッセージ
email_confirmation_subject.txt|その件名
password_reset_key_message.txt|パスワードリセットが申請されましたの確認メッセージ
password_reset_key_subject.txt|その件名

### account / file

|ファイル名|ページ内容|
|---|---|
email.html|メールアドレスの管理ページ
email_confirm.html|メール確認完了のページ
login.html|ログインページ
password_change.html|パスワード変更ページ
password_reset.html|パスワードリセットのメアド入力ページ
password_reset_done.html|パスワードリセットするメールを送信しました！ページ
password_reset_from_key.html|メールのリンククリック後のトークン確認ページ
password_reset_from_key_done.html|パスワード変更しましたページ
password_set.html|パスワード設定ページ
signup.html|サインアップページ
verification_sent.html|メアドでのサインアップ時の確認のメールを送信しましたページ

### socialaccount / file

|ファイル名|ページ内容|
|---|---|
snippets|socialaccount内にsnippetsフォルダを作る
provider_list.html|snippetsフォルダ内、外部アカウント名が表示される
connections.html|外部アカウント管理ページ

他に必要なページがある場合は同じようにコピーしてくればhtmlを編集できます！

### 編集の基本

"signup.html"の中身を少し解説します。

他のファイルも同じ感じなので対応できると思います。

```html:signup.html
<!-- ここを自分のbaseファイルに変える -->
{% extends "account/base.html" %}
<!-- これは翻訳機能 -->
{% load i18n %}

<!-- ヘッダーのタイトル -->
{% block head_title %}{% trans "Signup" %}{% endblock %}

{% block content %}
<!-- ページに表示されるタイトル -->
<h1>{% trans "Sign Up" %}</h1>

<!-- テキスト -->
<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>

<!-- フォーム -->
<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
    {% csrf_token %}
    {{ form.as_p }}
    {% if redirect_field_value %}
    <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
    {% endif %}
    <!-- ボタンのテキスト -->
    <button type="submit">{% trans "Sign Up" %} &raquo;</button>
</form>
{% endblock %}
```

`{% load i18n %}`は`{% trans "Sign Up" %}`と`{% blocktrans %}`の部分を翻訳してくれます。

翻訳ファイル内容は`locale`のフォルダの中に言語ごとに入ってるので興味があればチェックしてみてください。

日本語だけでOKなら消しちゃって日本語に変えちゃいましょう！

あとは、htmlを使って自分のサイトデザインと合わせていく作業になります。

何度も言いますがこの編集作業が一番大変なので、全部ではなく、一つずつ完成させるように意識を保ちましょうｗ

これで終わりますが、間違いがあったらコメントいただけると助かります！