今回は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 %}