关于类视图(登录)(个人备忘录)

Django将成为我的个人备忘录。

这次我们将总结一下关于类视图(登录)的内容。

开发环境 fā

操作系统:mac
编辑器:vscode
Python版本:3.10.9
Django版本:4.1.0

若欲将未登录用户无法运行的视图进行多重继承,
或给需要登录的视图加上装饰器。
@method_decorator(login_required)

准备好(复习AbstractBaseUser等内容)

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中进行相应的设置。

AUTH_USER_MODEL = 'accounts.Users'

# Application definition

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

创建视图、创建注册表单、创建网址、创建可进行跳转的HTML。

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 #のちに記述
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
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'),
]

<!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中指定要使用的表单。
在post方法中,获取到POST请求中的email和password,并使用authenticate方法进行验证,如果成功验证,则使用login方法进行登录,并进行重定向。
在UserLogoutView中,使用get方法接收请求,并使用logout方法进行注销,并进行重定向。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

def post(self, request, *args, **kwargs):
email = request.POST[’email’]
password = request.POST[‘password’]
user = authenticate(email=email, password=password)
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)。

做法有三种:
1. 在dispatch方法中定义method_decorator(login_required)。
2. 在整个类中定义method_decorator(login_required, name=’dispatch’)。
3. 通过多重继承将LoginRequiredMixin添加到类中。

导入的东西

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin
# 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)
当请求是GET请求时,发送一个GET方法的请求;当请求是POST请求时,发送一个POST方法的请求。这是为什么在请求时必须执行的处理方法。
スクリーンショット 2023-08-23 13.57.58.png
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属性POST了?next=后面的信息,这样处理会更加智能。

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')
    <input type="hidden" name="next" value="{{request.GET.next}}">
    #request.GET.nextで?next=の後の情報をviewに投げることができる

登录视图,注销视图

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。

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

class UserLogoutView(LogoutView):
    pass
forms.py中的username字段是使用EmailField,因为在models.py中的USERNAME_FIELD = ’email’设置了此字段来处理。

如果不指定登录后的跳转页面,将会出现页面找不到的错误。

LOGIN_REDIRECT_URL='/accounts/home'
LOGOUT_REDIRECT_URL='/accounts/user_login'

记住下次登录(更改会话保存时间)。

公式 –

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

公式:使用request.session.set_expiry(value)函数可以将会话的存储时间更改为传入的秒数。如果参数为0,则在关闭浏览器时会话将被删除。如果value是datetime或timedelta对象,则会话将在指定的日期时间被销毁。

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

在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)
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)
bannerAds