0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

クラスベースビュー(ログイン)について(自分用メモ)

Last updated at Posted at 2023-08-22

Django自分用メモになります

今回はクラスベースビュー(ログイン)についてまとめます

開発環境

OS:mac
エディタ:vscode
python:3.10.9
django:4.1.0

LoginRequiredMixin
ログインしていないと動かしたくないviewに多重継承させる
またはログインが必要なViewにデコレータをつける
@method_decorator(login_required)

下準備(AbstractBaseUserなどの復習)

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


class UserManager(BaseUserManager):
    def create_user(self,username,email,password=None):
        if not email:
            raise ValueError('メールアドレスをいれてください')
        user =self.model(
            username = username,
            email = email
        )
        user.set_password(password)
        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 Users(AbstractBaseUser,PermissionsMixin):
    username = models.CharField(max_length=150)
    email = models.EmailField(max_length=255,unique=True)
    is_active=models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS=['username']
    
    objects = UserManager()
    
    def __str__(self):
        return self.email+''+self.username
    
    def get_absolute_url(self):
        return reverse_lazy('accounts:home')

settings.pyに設定も忘れず

settings.py
AUTH_USER_MODEL = 'accounts.Users'

# Application definition

INSTALLED_APPS = [
    #(略)
    'accounts',
]

viewの作成 登録フォームの作成 urlの作成 遷移ができるhtmlの作成

views.py
from django.shortcuts import render
from django.views.generic.base import TemplateView,View
from django.views.generic.edit import CreateView,FormView
from .forms import RegistForm
class HomeView(TemplateView):
    template_name = 'home.html'
    
    
class RegistUserView(CreateView):
    template_name = 'regist.html'
    form_class = RegistForm
    
class UserLoginView(FormView):
    pass #のちに記述

class UserLogoutView(View):
    pass #のちに記述
forms.py
from django import forms
from django.contrib.auth import get_user_model
from .models import Users
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError

User=get_user_model

class RegistForm(forms.ModelForm):
    username=forms.CharField(label='名前')
    age = forms.IntegerField(label='年齢',min_value=0)
    email =forms.EmailField(label='メールアドレス')
    password = forms.CharField(label='パスワード',widget=forms.PasswordInput)
    confirm_password = forms.CharField(label='パスワード再入力',widget=forms.PasswordInput)
    
    class Meta:
        model=Users
        fields = ('username','age','email','password')
        
    def clean(self):
        cleaned_data=super().clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')
        if password !=confirm_password:
            raise ValidationError('パスワードが一致しません')
        
    def save(self,commit=False):
        user =super().save(commit=False)
        validate_password(self.cleaned_data.get('password'),user)
        user.set_password(self.cleaned_data.get('password'))
        user.save()
        return user
urls.py
from django.urls import path
from .views import(
    HomeView,RegistUserView,UserLoginView,UserLogoutView
)
app_name='accounts'

urlpatterns = [
    path('home/',HomeView.as_view(),name='home'),
    path('regist/',RegistUserView.as_view(),name='regist'),
    path('user_login/',UserLoginView.as_view(),name='user_login'),
    path('user_logout/',UserLogoutView.as_view(),name='user_logout'),
]

base.html
<!DOCTYPE html>
<html lang='ja'>
    <head>
        <meta charset='utf-8'>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    </head>
    <body>
        <nav class ='navbar navbar-expand-lg navbar-light bg-light'>
            <a class ='navbar-brand' href="{%url 'accounts:home'%}">ホーム</a>
            {% if user.is_authenticated%} {#ログインしているかどうかで表示を切り替える#}
            <a class ='navbar-brand' href="{%url 'accounts:user_logout'%}">ログアウト</a>
            {% else %}
            <a class ='navbar-brand' href="{%url 'accounts:user_login'%}">ログイン</a>
            <a class ='navbar-brand' href="{%url 'accounts:regist'%}">ユーザ登録</a>
            {% endif %}

        </nav>
        {% block content%}
        {% endblock %}
    </body>
</html>

ログインとログアウトの実装

views.pyで先ほど定義したUserLoginViewとUserLogoutViewの処理を書いていく。
ログインに関してはforms.pyでログインに必要な項目を記載したフォームをつくったら
FormViewを継承し、form_classで使うフォームに指定。
def postでPOSTが行われたそのemailとpasswordを取得しauthenticateで
存在するか確かめてTrueであればloginメゾットでログインし、redirectさせる
ログアウトもgetで受け取ってlogoutメゾットを使って、redirectさせるだけ

forms.py
class UserLoginForm(forms.Form):
    email=forms.EmailField(label='メールアドレス')
    password =forms.CharField(label='パスワード',widget=forms.PasswordInput)
views.py
class UserLoginView(FormView):
    template_name='user_login.html'
    form_class=UserLoginForm
    # success_url=reverse_lazy('accounts:home') #redirectさせるのでいらない
    
    def post(self,request,*arg,**kwargs):
        #POSTが行われたらemailとpasswordを取得して
        email = request.POST['email']
        password = request.POST['password']
        #そのユーザが存在するかauthenticateで確かめる
        user = authenticate(email=email,password=password)
        #userが存在し、かつ、is_activeがTrueの場合にログインを行う
        if user is not None and user.is_active:
            login(request,user)
        return redirect('accounts:home')

class UserLogoutView(View):
    
    def get(self,request,*args,**kwargs):
        logout(request)
        return redirect('accounts:user_login')

ログインしていないと実行させたくないを設定する方法(LoginRequiredMixin,login_required)

やりかたは3つ
・dispatchメゾットにmethod_decorator(login_required)を定義する方法
・クラス全体にmethodo_decorator(login_required,name='dispatch')を定義する方法
・LoginRequiredMixinをクラスに多重継承させる

importするもの

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin
views.py
# 2.@method_decorator(login_required,name='dispatch')
class UserView(LoginRequiredMixin,TemplateView): #3
    template_name = 'user.html'
    
    #1. @method_decorator(login_required) #ログインしていないと実行されない
    #dispatchをオーバーライド
    #dispatchとはgetリクエストならばgetをpostリクエストならpost処理を行うメゾット、だからリクエストの際必ず行われる
    def dispatch(self,*args,**kwargs):
        return super().dispatch(*args,**kwargs)

dispatchとはgetリクエストならばgetをpostリクエストならpost処理を行うメゾット、だからリクエストの際必ず行われる

ここでLoginRequiredMixinやlogin_requiredで弾かれた場合内部的にどのurlにアクセスしていて
page not found がでているのかというとデフォルトでhttp://127.0.0.1:8000/accounts/login/
にアクセスしていて、urls.pyにそんなページは存在しないからpage not foundとなる。
もし弾かれた場合にログイン画面に移動させたい場合はsettings.pyでLOGIN_URLを設定してあげる
スクリーンショット 2023-08-23 13.57.58.png

settings.py
LOGIN_URL='/accounts/user_login' #デフォルトのaccounts/login/をにaccounts/user_loginにリダイレクト先を変更する

?next= に対応させる(よりスマートなページ移行)

login_requiredまたはLoginRequiredMixinによって弾かれてかつ
settings.pyでLOGIN_URLでログインページにリダイレクトした場合、
ログインページに普通にアクセスするときと違い、urlパラメータに?next=/accounts/user/と
表示されていることがわかる。
?next=とはこのページに行こうとして弾かれてしまったので、ログインができたらこのページに行こうとしていたよという情報を残してくれている。
もしnext_urlがあればログインした時にホーム画面などに飛ぶのではなく、その行こうとしたページに飛ぶといった処理を
1.ログインするviewに追加することと、
2.ログインのhtmlにinputからvalueで?next=のあとの情報をPOSTするように設定すると、そのように実行されるのでスマートになる

views.py
class UserLoginView(FormView):
    template_name='user_login.html'
    form_class=UserLoginForm
    # success_url=reverse_lazy('accounts:home')
    
    def post(self,request,*arg,**kwargs):
        #POSTが行われたらemailとpasswordを取得して
        email = request.POST['email']
        password = request.POST['password']
        #そのユーザが存在するかauthenticateで確かめる
        user = authenticate(email=email,password=password)
        #userが存在し、かつ、is_activeがTrueの場合にログインを行う
        if user is not None and user.is_active:
            login(request,user)
        # ?nextがurlパラメータにあればそちらに移行する  #追加
        next_url =request.POST['next']
        if next_url:
            return redirect(next_url)
        return redirect('accounts:home')
user_login.html
    <input type="hidden" name="next" value="{{request.GET.next}}">
    #request.GET.nextで?next=の後の情報をviewに投げることができる

LoginView,LogoutView

from django.contrib.auth.views import LoginView #ログインするためのView

template_name= '' #表示するテンプレートの指定
authentication_form #ログイン認証に用いられるFormを指定(デフォルトはAuthenticationForm)

from django.contrib.auth.forms import AuthenticationForm #ログイン認証に用いられるForm(forms.pyで使うフォームに継承させる)
from django.contrib.auth.views import LogoutView #ログアウトするためのView

#settings.pyに設定する内容
LOGIN_REDIRECT_URL #LoginViewで次に遷移する先が指定されていなかった場合の遷移先のURL
LOGIN_URL #ログインしていない状態でlogin_requiredが指定されているViewを表示しようとした場合にリダイレクトされるView
LOGOUT_REDIRECT_URL #LogoutViewで次に遷移する先が指定されていなかった場合の遷移先のURL

LoginViewを継承してUserLoginViewを作り直す。

views.py
class UserLoginView(LoginView):
    template_name='user_login.html'
    authentication_form=UserLoginForm

class UserLogoutView(LogoutView):
    pass

ここでのusernameとはmodels.pyでのUSERNAME_FIELD = 'email'に設定したものを扱うのでEmailFieldをつかう

forms.py
class UserLoginForm(AuthenticationForm):
    #ここでのusernameとはmodels.pyでのUSERNAME_FIELD = 'email'に設定したものを扱うのでEmailFieldをつかう
    username= forms.EmailField(label='メールアドレス')
    password= forms.CharField(label='パスワード',widget=forms.PasswordInput())

ログインの遷移先を指定しないとpage not foundになる

settings.py
LOGIN_REDIRECT_URL='/accounts/home'
LOGOUT_REDIRECT_URL='/accounts/user_login'

次回のログインを記憶させる(セッションの保存時間を変更する)

公式

settings.py
SESSION_COOKIE_AGE #セッションの保存時間(秒) デフォルトは1209600(2週間)

公式
さらにrequest.session.set_expiry(value):セッションの保存時間を引数の秒に変更する。
引数が0の場合、ブラウザを閉じるとセッションがなくなる。もしvalueがdatetimeまたはtimedeltaオブジェクトならば指定された日時に破棄される
公式

settings.py
SESSION_COOKIE_AGE=5 #5秒でセッションが解除される

ログイン状態を保持するボタンをforms.pyに追加

forms.py
class UserLoginForm(AuthenticationForm):
    #ここでのusernameとはmodels.pyでのUSERNAME_FIELD = 'email'に設定したものを扱うのでEmailFieldをつかう
    username= forms.EmailField(label='メールアドレス')
    password= forms.CharField(label='パスワード',widget=forms.PasswordInput())
    remember= forms.BooleanField(label='ログイン状態を保持する',required=False)
views.py
class UserLoginView(LoginView):
    template_name='user_login.html'
    authentication_form=UserLoginForm
    
    #ログイン状態を保持する(session時間を変更する)
    def form_valid(self,form):
        #rememberを取り出してTrueであれば秒数を更新
        remember=form.cleaned_data['remember']
        if remember:
            self.request.session.set_expiry(1200000)
        return super().form_valid(form)
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?