166.登录、退出登录以及登录限制案例

权限和分组

登录、注销和登录限制

1. 登录

在使用authenticate进行验证后,如果验证通过了,那么就会返回一个user对象,拿到user对象之后,可以使用django.contrib.auth.login进行登录,部分示例代码如下:
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
    login(request, user)

2. 注销:

注销、或者是退出登录,我们可以通过django.contrib.auth.logout来实现,它会调用flush()方法清除掉用户的session数据。部分示例代码如下:
from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    return HttpResponse('Success!')

3. 登录限制:

有时间,某个视图函数是需要经过登录才能访问的,那么我们就可以通过django.contrib.auth.decorators.login_required装饰器实现。示例代码如下:
from django.contrib.auth.decorators import login_required

<!--可以指定登录的url-->
@login_required(login_url='/login_logout/')
def profile(request):
    return HttpResponse('个人中心!')

项目完整代码如下

(1)views.py文件中示例代码如下:

from django.http import HttpResponse
from djang.shortcuts import render, redirect, reverse
from .models import User
from .forms import login_logout_Form
from django.contrib.auth import authenticate, auth
from django.views import View


<!--1.首先定义一个添加用户信息的视图-->
def addview(request):
    <!--1. 添加普通用户-->
    user = User.objects.create_user(username='孤烟逐云', telephone='18833331111', password='111111')
    user.email = 'ant@qq.com'
    user.save()
    return HttpResponse('successful!')
    
    <!--2. 添加超级用户-->
    user = User.objects.create_superuser(username='云中云', telephone='18833332222', password='111111')
    user.email = 'ant01@qq.com'
    user.save()
    return HttpResponse('success!')
 

<!--表单的形式提交用户数据-->
<!--2. 之后定义一个首页类视图,如果是get请求,返回 一个模板;如果是post请求就采用表单验证提交的数据的合法性,如果合法就添加到数据库中-->
class login_logout(View):

    def get(self, request):
        return render(request, 'login_logout.html')
        
    def post(self, request):
        form = login_logout_Form(request.POST)
        if form.is_valid():
            telephone = form.clean_data.get('telephone')
            password = form.clean_data.get('password')
            remember = form.clean_data.get('remember')
            user = authenticate(request, username=telephone, password=password)
            <!--如果验证成功就会返回一个user对象,验证不成功,就返回None-->
            if user and user.is_active:
                login(request, user)
                if remember:
                    <!--设置过期时间通过session.set_expiry(),设置为None的话,就会使用全局的-->
                    request.session.set_expiry(None)
                else:
                    request.session.set_expiry(0)
                return HttpResponse('登录成功!')
            else:
                return HttpResponse('用户名或密码不正确!')
        <!--如果表单没有验证成功-->
        else:
            print(form.errors.get_json_data())
            return redirect(reverse('login_logout:login_logout'))       

(2)forms.py文件中示例代码如下:

from django import forms
from .models import User
from django.contrib.auth import get_user_model

class login_logout_Form(forms.ModelForm):
    remember = forms.IntegerField(required=False)
    class Meta:
        model = get_user_model()
        fields = ['telephone', 'password']
    <!--如果你想要验证模型User中定义的所有字段, 就可以指定fields = '__all__'-->

(3)在urls.py文件中进行视图函数与url之间的映射。需要注意的是,一定要将类转换为类视图,即调用as_view()转换,示例代码如下:

from django.urls import path
from . import views


app_name = 'login_logout'


urlpatterns = [
    path('', views.login_logout.as_view(), name='login_logout'),
    path('add/', views.addview, name='add')
]
(1)否者的话,就会出现TypeError: init() takes 1 positional argument but 2 were given.
(2)并且需要注意的是,在调用Django内置的验证方法authenticate()验证的时候,第一个参数一定要传入request。

(4)这里使用的User模型,是我们通过继承AbstractBaseUser重写的User模型,对模型的字段进行了大幅度的改动,示例代码如下:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.contrib.auth.models import AbstractUser
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate


class UserManager(BaseUserManager):
    def _create_user_(self, telephone, username, password, **kwargs):
        if not telephone:
            raise ValueError('请输入密码!')
        if not username:
            raise ValueError('请输入用户名!')

        # 注意,这里是self.model代表的是当前的User模型而不能写self.User()因为UserManager没有这个属性
        user = self.model(telephone=telephone, username=username)
        user.set_password(password)
        user.save()
        return user

    def create_user(self, telephone, username, password, **kwargs):
        kwargs['is_super'] = False
        return self._create_user_(telephone=telephone, username=username, password=password, **kwargs)

    def create_superuser(self, telephone, username, password, **kwargs):
        kwargs['is_super'] = True
        return self._create_user_(telephone=telephone, username=username, password=password, **kwargs)


# PermissionsMixin: 是定义用户时定义用户权限,必须继承
# AbstractBaseUser:User的基类其中涉及的字段最少,如果先定义自己的User模型,
# 将大部分Django中默认的字段删除的话,就可以继承该类
# AbstractBaseUser类中定义的字段有:password,last_login,is_active,is_super
class User(AbstractBaseUser, PermissionsMixin):
    # 必须设置的两个字段username,email
    username = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    telephone = models.CharField(max_length=11, unique=True)

    is_active = models.BooleanField(default=True)

    # 唯一性验证设置为telephone
    USERNAME_FIELD = 'telephone'
    # REQUIRED_FIELDS为空的列表代表的是,在验证用户登录的时候只需要输入password和USERNAME_FIELD指定的字段就行了
    # 如果还想在验证用户的时候输入其他的字段,那么就可以将该字段设置在该列表中。
    REQUIRED_FIELDS = []

    # 以上自定义的User模型上并没有objects方法,所以我们需要自定义
    objects = UserManager()

    def get_full_name(self):
        return self.username

    def get_short_name(self):
        return self.username

(5)如果使用这种继承AbstractBaseUser的方式进行重写Django内置的User模型的话,就需要告诉Django,现在要使用的是自己定义的User模型,这就需要在settings.py文件中配置:

AUTH_USER_MODEL = 'login_Logout.User'

(6)views.py文件中涉及的模板login_logout.html中的实例代码如下:

<form action="" method="post">
{% csrf_token %}
    <table>
        <tbody>
        <tr>
            <td>手机号:</td>
            <td><input type="text" name="telephone"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="text" name="password"></td>
        </tr>
        <tr>
            <td>
                <label for="">
                    <input type="checkbox" name="remember" value="1">
                </label>
            </td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="提交"></td>
        </tr>
        </tbody>
    </table>
</form>
需要注意的是,如果你在提交表单信息的时候没有加上{% csrf_token %}或者是,就会出现403的csrf禁止页面,所以可以选择一种方式进行添加。
之后就可以输入url进行登录验证了,这时会在pycharm终端打印出错误信息:{'telephone': [{'message': 'User with this Telephone already exists.', 'code': 'unique'}]}。因为在models.py文件中的模型User中定义的telephone字段就是唯一的,即unique=True,而且在forms文件中继承了User所有的字段,所以此时在验证的fields中指定telephone的话,就会验证数据库中的telephone的唯一性。
解决办法就是在forms.py文件的验证字段fields中不要指定telephone,可以重新定义一个字段telephone,示例代码如下:
class Login_logout_Form(forms.ModelForm):
    remember = forms.IntegerField(required=False)
    telephone = forms.CharField(max_length=11)
    class Meta:
        model = get_user_model()
        fields = ['password']
此时,再次在浏览器中的表单中输入用户的信息telephone和password就可以登录成功了。

(7)定义个人中心的视图,示例代码如下:

def profile(request):
    return HttpResponse('个人中心!')

在子urls.py文件中进行一层视图函数与url之间的映射,示例代码如下:

path('profile/', views.profile, name='profile'),

(8)此时,用户登录与否我们都可以通过url访问个人中心页面,这个有些不符合情理,我们可以将个人中心页面定义为只有用户登录之后,才能访问。

from django.contrib.auth.decorators import login_required
<!--login_required是一个定义好的装饰器-->

<!--装饰器中的login_url指定登录页面的url-->
@login_required(login_url='/login_logout/')
def profile(request):
    return HttpResponse('个人中心!')
此时,我们如果在没有登录的情况下,去访问个人中心页面,就会跳转到登录页面url,http://127.0.0.1:8000/login_logout/?next=/login_logout/profile/, 登录之后就会重定向到个人中心页面。

(9)定义退出登录的视图logout_view,示例代码如下:

from django.contrib.auth import logout

def logout_view(request):
<!--logout()函数会将session中记录的sessionid清空,其实调用的就是flush()方法,sessionid的到期时间也会变为浏览器关闭就到期-->
    logout(request)
    return HttpResponse("退出登录!")
posted @ 2020-02-23 15:31  一笑而过~一笑奈何  阅读(411)  评论(0编辑  收藏  举报