見出し画像

【81日目】Django_新規ユーザーの作成方法その2_プログラミング学び日記

 このnoteは、31歳未経験からエンジニアを目指して勉強していく記録を綴っているものです。現在はAdTechでカスタマーサクセスを担当しつつ、色んなチャンスに恵まれ、CS業務や子育てと並行しながらチャレンジしています。

 これからプログラミングを始める方にとってのTipsやモチベアップに繋げられるように頑張りたいと思っています。
--------------------------------------------


本日は前回の続きで、Djangoでの新規ユーザー登録ページの作成方法に関してです。今回はメール認証の設定をやっていきます。

前回の記事はこちら


こちらの動画の後半部分で勉強させていただいています。


こんな感じの流れでメール認証ができるようになります。

認証前にログインしようとした場合
メールの認証用URLにアクセスしたとき
認証後にログインした場合


メール認証とは?

何かのサイトで新規ユーザー登録などをする際、登録完了するために一度メールを受け取り、メール内のURLをクリックすることがあると思います。今回実装していくのはそういう機能です。


forms.pyに認証メールのタイトル/本文/認証用URLを設定

まずはメール内容を設定します。URLを生成するところが少し複雑ですが、これはほとんどテンプレらしく、細かく理解する必要はないそうです。メールの送信から認証完了まで、大部分の処理はforms.pyに定義します。

[forms.py]

# ユーザー認証用の、そのユーザー固有のトークンを生成して本人であることを確認するためのもの 
from django.contrib.auth.tokens import default_token_generator
# 以下の4つは文字列やデータを相互に変換するためのもの
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode

 ~中略~

# 認証用メールのタイトルと本文のテンプレート
subject = "登録確認"
message_template = """
ご登録ありがとうございます。
以下のURLをクリックして登録完了してください。

"""

# 認証用メールに載せるURLを定義する関数(idやtokenの生成方法を細かく理解する必要は今はない)
def get_activate_url(user):                                   # userを渡すと
    uid = urlsafe_base64_encode(force_bytes(user.pk))         # user idと
    token = default_token_generator.make_token(user)          # tokenを生成して
    # こんな感じのurlを作ってくれる(formatはPythonのformat関数)
    return settings.FRONTEND_URL + "activate/{}/{}/".format(uid, token)  


新規登録用フォームに認証用メールを送信できる設定を追加

前回の記事で作成したSignUpFormを編集して、認証用メールを送信できるようにしています。このクラスの中のsaveメソッド内で、前段で定義した「get_activate_url()」を呼び出してメールを作成し、送信するところまで設定しています。

[forms.py]
 
# UserCreationFormはDjangoが元々用意している新規登録用のフォーム
class SignUpForm(UserCreationForm):
    class Meta:
        model = User
        fields = ("username", "email", "password1", "password2")
    
    # ユーザーが投稿した時に、ユーザーのモデルを保存するだけでなく、認証用メールを送信するための設定
    def save(self, commit=True):
        user = super().save(commit=False)        # commit=FalseとしてDBに保存しないようにしておく
        user.email = self.cleaned_data["email"]

        # メール認証するまでログイン不可にする
        user.is_active = False
        
        # saveメソッドを定義するところでcommit=Trueにしているから以下は必ず実行される
        if commit:
            user.save()    # emailのバリデOKでセットされているためここでデータベースに保存(modelのsave)
            activate_url = get_activate_url(user)      # 前段で定義した関数で認証用のurlを生成する
            message = message_template + activate_url
            user.email_user(subject, message)          # ユーザーにメール送信するのはこれだけでOK。一人だけにメール送信するのであれば認証に限らずこの方法が使える。
        return user


正しいユーザーが認証メールをクリックした場合、そのユーザーを有効化するための関数を設定

後ほどパスの設定をすると明確になりますが、認証メールのURLをクリックするとユーザーIDとトークンがそこにくっ付いてきます。そしてそのIDとトークンは(これも後ほど設定する)ビューで受け取ることができます。

以下はそうやって受け取ったIDとトークンが正しいかどうか検証して、正しければユーザーを有効化するための関数です。これはforms.pyに定義します。

[forms.py]
 
# ユーザーを有効化するための関数
# 認証リンクをクリックしたときのトークンを検証してユーザーを有効化する
def activate_user(uidb64, token):
    try:
        # decodeの関数でuidを復元
        uid = urlsafe_base64_decode(uidb64).decode()
        # 復元したuidのユーザーを探し出す
        user = User.objects.get(pk=uid)
    # ユーザーが見つからなければ何もせずにFalseを返す
    except Exception:
        return False

    # ユーザーが見つかって、かつトークンのチェックもOKだったら
    if default_token_generator.check_token(user, token):
        # ユーザーを有効化して
        user.is_active = True
        # データベースに保存して
        user.save()
        # Trueを返す
        return True
    # 失敗した場合はFalseを返す
    return False


IDとトークンを受け取って、検証結果をテンプレートに返すためのビューを作成

ようやくビューの作成です。今回作成するビューの役割は、メールで送信した認証用のURLにアクセスしてきたユーザーのIDとトークンを取得して、formsで定義したactivate_user関数を呼び出して検証し、その検証結果を返す(OKならTrueを返す)という感じです。

[views.py]
 
class ActivateView(TemplateView):
    template_name = "registration/activate.html"

    # URLでuidとtokenを渡すようになっている。この2つは以下のようにgetメソッドで取得できる。(selfとrequestは常に必要)
    # このActivateViewが呼ばれるときのURLにくっ付いてくる(urls.pyで設定している)
    def get(self, request, uidb64, token, *args, **kwargs):
        # 取得したuidとtokenをforms.pyで作ったactivate_userに渡すことで、認証トークンを検証する
        # 関数の結果がTrue/Falseで返ってくるためそれが入る
        result = activate_user(uidb64, token)
        # 以下はTemplateViewそのもののgetで、それにresultを渡してやるとコンテクストとしてテンプレートのほうにTrue/Falseの結果を渡してくれる
        # get_context_dataとかで定義していたものの別のやり方。
        return super().get(request, result=result, **kwargs)


認証メールのリンクにアクセスしたら上記ビューに飛ばすためのパスを設定

いつも通りビューのパスを設定します。認証用のURLにアクセスがあったら、上記のActivateViewに飛ぶように設定します。

[urls.py]
 
from registration import views

urlpatterns = [
 
    ~中略~ 

    path('activate/<uidb64>/<token>/', views.ActivateView.as_view(), name='activate'),
]


認証メールのリンクにアクセスした際にユーザーに表示されるテンプレートを作成

これも流れとしてはいつも通りです。ユーザーに表示するテンプレート作成します。認証後、ログイン画面に飛べるようにリンクを設定しておきます。

[activate.html]
 
{% extends "base.html" %}
{% block main %}
{% if result %}
    <p>認証に成功しました。</p>
    <p><a href="{% url 'login' %}">ログイン</a></p>
{% else %}
    <p>無効なリンクです。</p>
{% endif %}
{% endblock %}


明日からは、以前作成したTodoアプリのブラッシュアップを色々やってみようと思います。



これまで修了したコース等

【YouTube_Django関係】
Pythonでウェブサービスを作ろう! #1
テンプレートをマスターしよう! #2
静的ファイルを配信しよう !#3
本番公開しよう! #4
データベースと接続しよう! #5
ブログを作って学ぶモデル入門! #6
これが汎用ビューの力! #7
Djangoフォームを自由自在に操ろう! #8
djagoを最大限使って効率よくログインを作ろう! #9
ログイン完成!サインアップ & メール認証 #10
データベースマイグレーション前編 #15
データベースマイグレーション後編 #16

【Paiza】
Aランクレベルアップメニュー 24/49問
データセット選択メニュー 4/17問
配列メニュー      59/64問
ループメニュー1 20/20問
ループメニュー2 12/20問
条件分岐メニュー    25/25問
二重ループメニュー   19/19問
配列活用メニュー    26/26問
文字列処理メニュー   30/30問
Bランクレベルアップメニュー 62/62問
Cランクレベルアップメニュー 30/30問
ランクB合格
ランクC合格

【書籍/ブログ】
Django入門 | 初心者でも1時間でWebアプリ(Todoアプリ)を作成するコース
基礎からのMySQL     514/514頁
Web技術の基本      189/189頁 ※2周目中
京大のPython教科書    116/201頁
Pythonデータベースプログラミング 194/194頁
Pythonエンジニアファーストブック読了

【Progate】
Python Ⅰ~Ⅴ
Python アプリ版 コースⅠ~Ⅴ
SQL Ⅰ~ Ⅳ
SQL アプリ版 コースⅢ
HTML&CSS 初級編

【その他】
Pythonの環境構築
VSCodeの環境構築
MySQLの環境構築(MAMP)
Git / GitHubの環境構築
HEROKUの環境構築

この記事が気に入ったらサポートをしてみませんか?