djangoでメールアドレスでのログインを実現する方法

djangoでメールアドレスでのログインを実現する方法

今回はdjangoでユーザー名とパスワードではなく、メールアドレスとパスワードでログイン(新規登録)を実現する方法を紹介します。


地味に苦戦したのが、models.pyのis_active = models.BooleanField(default=True)を忘れていて、ログインがはじかれてしまうこと。メール認証などを行う場合は、default=Falseにしておいて認証後にTrueにすることで認証機能を実現できますが、この機能を入れない場合は、最初からTrueにしておかないとログインできません。

#models.py

from django.db import models
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser, PermissionsMixin
)


class UserManager(BaseUserManager): # ☆3

    def create_user(self, username, email, password=None):
        if not email:
            raise ValueError('Enter Email!')
        user = self.model(
            username=username,
            email=email,
        )
        user.set_password(password)
        user.is_active = True
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password=None):
        user = self.model(
            username=username,
            email=email,
        )
        user.set_password(password)
        user.is_staff = True
        user.is_active = True
        user.is_superuser = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser, PermissionsMixin): # ☆1

    # 使いたいFieldを追加

    lang_select = (
        ("Japanese", "日本語"),
        ("English", "English"),
        ("SimpleChinese", "簡体"),
        ("TraditionalChinese", "繁体"),
    )

    username = models.CharField(max_length=20, verbose_name="username")
    email = models.EmailField(max_length=255, unique=True, verbose_name="email")
    lang = models.CharField(max_length=20,verbose_name="language", choices=lang_select, blank=False, null=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email' # このテーブルのレコードを一意に識別
    REQUIRED_FIELDS = ['username'] # スーパーユーザ作成時に入力する

    class Meta:
        db_table = 'users'

    def __str__(self):
        return self.email

新規登録とログインで意識することはパスワードをハッシュ化するためにviews.pyでuser.set_password(form.cleaned_data[‘password’])を入れることです。

#views.py

from django.contrib.auth.views import LoginView as AuthLoginView
from django.contrib.auth.views import LogoutView as AuthLogoutView
from django.views.generic.base import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, UpdateView
from .forms import SignupForm, UserChangeForm
from .models import User
from django.shortcuts import redirect
from django.contrib import messages



class SigninView(AuthLoginView):
    template_name = "accounts/signin.html"
    success_url = reverse_lazy('reservations:reservation')

signin = SigninView.as_view()



class SignoutView(AuthLogoutView):
    template_name = "accounts/signout.html"
    success_url = reverse_lazy('reservations:reservation')

signout = SignoutView.as_view()



class SignupView(CreateView):
    form_class = SignupForm
    template_name = "accounts/signup.html" 
    success_url = reverse_lazy('reservations:reservation')
    
    #登録に失敗した時に前回のデータを自動入力しておく
    def get_initial(self):
        initial = super().get_initial()
        form_data = self.request.session.get('form_data')
        if form_data:
            initial.update(form_data)
            return initial

    def post(self, request, *args, **kwargs):
        form = SignupForm(request.POST)
        email = request.POST.get("email")
        #同じメールアドレスがすでに登録されているか確認
        if User.objects.filter(email=email).exists():
            self.request.session['form_data'] = request.POST
            messages.error(self.request, "このメールアドレスはすでに登録されています")
            return redirect('accounts:signup')
        elif form.is_valid():
            user = form.save(commit=False)  # 新しいユーザーオブジェクトを取得
            user.set_password(form.cleaned_data['password'])  # パスワードをハッシュ化
            user.save()  # 保存
            self.request.session['form_data'] = None
            return redirect('reservations:reservation')
        else:
            # フォームが無効な場合の処理
            self.request.session['form_data'] = request.POST
            messages.error(self.request, "登録に失敗しました")
            return redirect('accounts:signup')

    
signup = SignupView.as_view()


class ProfileView(LoginRequiredMixin, TemplateView):
    template_name = "accounts/profile.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        user = self.request.user
        context['user'] = user
        return context
    
profile = ProfileView.as_view()



class UserChangeView(LoginRequiredMixin, UpdateView):
    template_name = 'accounts/changeinfo.html'
    form_class = UserChangeForm
    success_url = reverse_lazy('accounts:profile')

    def get_initial(self):
        initial = super().get_initial()
        if self.request.user.is_authenticated:
            initial['username'] = self.request.user.username  
            initial['email'] = self.request.user.email
            initial['lang'] = self.request.user.lang
        return initial
    
    def get_object(self):
        return self.request.user

changeinfo = UserChangeView.as_view()
#forms.py

from django import forms
from .models import User

class SigninForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)
    class Meta:
        model = User
        fields = ['email', 'password']
        widgets = {
            'email': forms.EmailInput(attrs={'required': True}),
        }
        
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['email'].widget.attrs['input'] = 'email'
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control col-12'


class SignupForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)
    class Meta:
        model = User
        fields = ['username', 'email', 'password', 'lang']
        widgets = {
            'username': forms.TextInput(attrs={'required': True}),
            'email': forms.EmailInput(attrs={'required': True}),
            'lang': forms.Select(attrs={'required': True}),
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control col-12'


class UserChangeForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'lang']

    def update(self, user):
        user.username = self.cleaned_data['username']
        user.email = self.cleaned_data['email']
        user.lang = self.cleaned_data['lang']
        user.save()
{% extends "accounts_base.html" %}

{% load static %}


{% block title %}SignIn{% endblock %}


{% block accounts_option %}
    <form class="form" action="" method="POST">
        {% csrf_token %}
        <h2>Sign In</h2>
        {% for fields in form %}
        <div class="form-group">
            <label for="">{{ fields.label_tag }}</label>
            {{ fields }}
            {% if fields.help_text %}
                    <span class="helptext">{{ fields.help_text }}</span>
                {% endif %}
            {{ fields.errors }}
        </div>
        {% endfor %}
        <button type="submit" class="btn btn-primary">Sign Up</button>
    </form>
    <div class="signup mt-5">
        <p>Don't have an account yet? <a href="{% url 'accounts:signup' %}">Sign Up</a></p>
    </div>
{% endblock %}

Comments

No comments yet. Why don’t you start the discussion?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です