从零开始的Django框架入门到实战教程(内含实战实例) - 07 用户登录界面(含网页请求基础知识点;登陆界面网页制作;使用中间件登录验证;用户注销、用户信息展示功能实现 详解)(学习笔记)

11 篇文章 53 订阅


  Django是目前比较火爆的框架,之前有在知乎刷到,很多毕业生进入大厂实习后因为不会git和Django框架3天就被踢掉了,因为他们很难把自己的工作融入到整个组的工作中。因此,我尝试自学Django并整理出如下笔记。
  完成了用户登录验证的部分流程。内容主要包括:

  1. 网页请求知识点概述,cookie和session概述
  2. 用户登陆界面网页制作,后端连接,cookie验证,保存session
  3. 利用中间件完成用户身份校验,防止直接通过url访问网页绕开用户登陆界面
  4. 登录后用户注销
  5. 登录后右上角展示用户信息

1. 前置知识

1.1. http请求

  拿我们平时的操作打比方,我们平时发送的请求都是下图这样的,是http请求,一般为无状态的短链接

在这里插入图片描述

  那无状态是什么?短链接又是什么?

  我们知道浏览器会向服务端发起请求,服务端在接收到请求之后经过一系列的处理最后返回一条响应。短链接就是用来断开这个请求与响应的。无状态就意味着每次浏览器向服务器发出请求都不带有以前的状态,不管浏览器是第几次发出请求,服务器都按照第一次收到请求对待。

1.2. cookie

  与http请求的无状态不一样,cookie就是我们的一种状态。在访问网站时,浏览器会向网站发送一个请求,网站的回复的响应包括响应头和响应体两部分,其中响应体就是展现在网页上的东西,响应头中就包括了cookie。这个cookie在被浏览器接收后就会被保存到浏览器内,在下一次访问这个网站时,浏览器发送的请求中就会包含这个cookie。由此我们知道cookie的两个特点:

  1. 本质上,cookie是保存在浏览器上的键值对(也会存在文件中)
  2. 在浏览器发送请求时,会自动携带cookie

  我们的想法由此而来:我们可以通过cookie做一个浏览器的凭证,代表这个浏览器是否是第一次访问我们网站,再基于这个完成接下来一系列的用户认证操作。

1.3. session

  我们知道网页会给用户发放cookie,那网页自己是怎么确定这个cookie是不是自己发放的呢?网页拿这个cookie有什么用呢?这个就涉及到了我们接下来用的session

  session是一类东西的统称,可以是数据库,也可以是redis,也可以是文件等等。里面的内容就是cookie以及这个cookie对应的变量值。

  举一个例子,我的浏览器访问了这个网页,网页给浏览器发放了一个cookie:k=6666,并在session中给这个cookie保存了name=Olsen,VIP=5的值。下一次我访问的时候,浏览器告诉网页我的cookie是k=6666,网页就可以通过session中的键值对确定我的身份信息和VIP等级,从而提供相应的权限和服务。

2. 用户登录

  知道cookie和session的用法之后,我们就可以完成这么一个功能了。

2.1. 前端界面

  首先是需要做出前端界面,还是三步走(urls.py, views.py, html文件):

  在诸多已有的界面中加入我们的登录界面(urls.py)

"""Employee_Management URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from application01.views import depart, num, user, admin, account

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('depart/list', depart.depart_list),
    path('depart/add', depart.depart_add),
    path('depart/del', depart.depart_del),
    path('depart/<int:nid>/edit', depart.depart_edit),
    path('user/list', user.user_list),
    path('user/add', user.user_add),
    path('user/madd', user.user_madd),
    path('user/<int:nid>/edit', user.user_edit),
    path('user/<int:nid>/del', user.user_del),
    path('num/list', num.num_list),
    path('num/add', num.num_add),
    path('num/a', num.num_a),
    path('num/<int:nid>/edit', num.num_edit),
    path('num/<int:nid>/del', num.num_del),
    path('admin/list', admin.admin_list),
    path('admin/add', admin.admin_add),
    path('admin/<int:nid>/edit', admin.admin_edit),
    path('admin/<int:nid>/del', admin.admin_del),
    path('admin/<int:nid>/reset', admin.admin_reset),
    path('login/', account.login),			# 登录界面
]

  因为views太多了,我将原来的views.py文件拆成了几部分,我们这次对views的操作都是在account.py中进行的。

在这里插入图片描述

  放个最简单的展示操作,够用就行。

# -*- coding:utf-8 -*-
from django.shortcuts import render

# 登录界面
def login(request):
    return render(request, 'login.html')

  html界面就随便在网上剽一个不要钱的登录界面,放上奥妹美美的照片即可。

在这里插入图片描述

2.2. 数据传输

  因为用户登录的操作仅仅是将用户写入的数据与数据库中已有的数据进行比较,并不是写入数据,所以这里我们就不用大动干戈地使用ModelForm了,直接用Form完成即可。当然ModelForm也可以完成,如果想了解具体的用法可以看03 学习笔记 4.3Django组件优化新建用户

  当然,我们这部分代码主要以学习知识点为目的,在真正开发的时候还是有很多优化的地方的。比如说有的事情能让客户端自己做的就没必要让服务器来干。

  account.py部分需要改一波,这里我们暂时不像以前那样吧LoginForm放到models文件夹中了。这段代码主要完成的工作有:

  1. 创建LoginForm用于接收传入的信息
  2. 判断传入数据是否为空
  3. 将传入的密码转化为密文,并于数据库中的数据比对校验
  4. 返回错误报错信息
from django.shortcuts import render, HttpResponse
from django import forms
from application01.utils.encrypt import md5
from application01 import models


class LoginForm(forms.Form):
    username = forms.CharField(label='USERNAME',
                               widget=forms.TextInput(attrs={
                                   'name': "username",
                                   'class': "border-item",
                                   'autocomplete': "off"}),
                               required=True  # 这是默认的,代表必填
                               )
    password = forms.CharField(label='PASSWORD',
                               widget=forms.PasswordInput(  # 用我们白嫖过来的样式
                                   {'name': "password",
                                    'class': "border-item",
                                    'autocomplete': "off"},
                                   render_value=True    # 保留密码
                               ),
                               required=True  # 这是默认的,代表必填
                               )

    # 返回密文
    def clean_password(self):
        pwd = self.cleaned_data.get('password')
        return md5(pwd)

    # 这部分纯粹就是尝试这个用法,在这个小问题上没有特别大的实际作用
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs["placeholder"] = field.label


# 登录界面
def login(request):
    if request.method == 'GET':
        form = LoginForm()
        return render(request, 'login.html', {"form": form})
    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 这里是没有models.save的,但是form.cleaned_data
        # 直接用字典形式,不用一个一个写
        admin_exist = models.Admin.objects.filter(**form.cleaned_data).first()
        # 密码错误
        if not admin_exist:
            form.add_error('password', '用户名或密码错误')
            return render(request, 'login.html', {'form': form})
        return HttpResponse(form.cleaned_data['username'] + '登录成功')
    return render(request, 'login.html', {"form": form})

  login.html的部分代码如下。主要注意Django在html中的应用。

<body>
<div class="form-wrapper">
    <div class="header">
        login
    </div>
    <form method="post">
        {% csrf_token %}
        <div class="input-wrapper">
            <div class="border-wrapper">
                <!--            <input type="text" name="username" placeholder="username" class="border-item" autocomplete="off">-->
                {{ form.username }}
            </div>
            <div style="text-align: center; width: calc(100% - 4px);">{{ form.username.errors.0 }}</div>
            <div class="border-wrapper">
                <!--            <input type="password" name="password" placeholder="password" class="border-item" autocomplete="off">-->
                {{ form.password }}
            </div>
            <div style="text-align: center; width: calc(100% - 4px);">{{ form.password.errors.0 }}</div>
        </div>
        <div class="action">
<!--            <div class="btn" >login</div>-->
            <input type="submit" class="btn block" value="LOGIN"></input>
        </div>
    </form>
    <div class="icon-wrapper">
        <i class="iconfont icon-weixin"></i>
        <i class="iconfont icon-qq"></i>
        <i class="iconfont icon-git"></i>
    </div>
</div>
</body>

  效果如下:

2.3. 加入cookie和session

  之前多次提到cookie,在这里终于用到了。原来是件挺麻烦的事情的,但是Django内部都帮我们封装好了,我们最后只需要一句话就可以完成:

# 登录界面
def login(request):
    if request.method == 'GET':
        form = LoginForm()
        return render(request, 'login.html', {"form": form})
    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 这里是没有models.save的,但是form.cleaned_data
        # 直接用字典形式,不用一个一个写
        admin_exist = models.Admin.objects.filter(**form.cleaned_data).first()
        # 密码错误
        if not admin_exist:
            form.add_error('password', '用户名或密码错误')
            return render(request, 'login.html', {'form': form})
        # 生成随机cookie原来还是比较麻烦的事情,但是Django帮我们简化了
        # 这一行代码直接完成了cookie的随机字符串的生成,将随机字符串保存到session中并附上响应的值
        request.session['info'] = {'id': admin_exist.id, 'username': admin_exist.username}
        return redirect("/admin/list")
    return render(request, 'login.html', {"form": form})

  查看下我们操作的效果:

在这里插入图片描述

  session的数据是被存在数据库中的,我们访问数据库查看。

在这里插入图片描述

3. 验证登录(中间件)

  在2. 用户登录中我们完成了用户登录界面的制作,但是在实际应用中这样显然是不够的,因为用户完全可以绕开我们的登录界面,从而直接访问其他的界面。因此,我们需要加入中间件来来解决这个问题。

  我们当然不可能每个页面之前加入一个判断,确定访问的浏览器是否已经登录,这样非常的机械,显然页太low了。这里我们利用Django中的另一个组件完成这项功能,这个东西就是中间件。

3.1. 中间件

  中间件有点类似于servlet里的filter或者是vue里面的路由守卫,我们发出的指令在Djongo中如果想要被执行,就需要通过一个又一个的中间件才能够完成,就像一个食品想要被摆上货架,需要经过层层检查。

  当然不止是进入网站需要经过中间件,出网站也需要经过中间件。在Django中,中间件就是一个类,进入是对应的就是process_request方法,出网站的时候对应的就是process_response方法。

  这么看来,我们确认登录的环节确实很适合用中间件来检验。

  先熟悉一下Django中的中间件应用(定义 -> 应用 -> ):

  • 定义中间件只需要在一个文件夹中写中间件,中间件必须要有process_requestprocess_response方法。
  • 应用中间件有点像urls.py中添加网页路径,中间件需要在settings.py中注册中间件,在已有的MIDDLEWARE列表中加入新的中间件。
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

3.2. 登录验证

  首先我们需要先定义一个中间件,我把它放在新的文件夹中:

在这里插入图片描述

  user_in.py

# -*- coding:utf-8 -*-
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect

class UserInMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 获取当前用户请求的url,如果访问的是login直接放行,不然会死递归
        if request.path_info == '/login':
            return
        # 读取当前用户的session信息
        info_dict = request.session.get('info')
        # 证明用户已经登录了
        if info_dict:
            return
        # 证明用户没登录
        return redirect('/login')

  再将它放入到settings.py里面的路径中

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'application01.middleware.user_in.UserInMiddleware'
]

  效果如下:

在这里插入图片描述

4. 用户注销

  很自然的,有登录就有注销,我们显然不能要求用户在每次退出的时候都进入到浏览器的设置中,手动消除浏览器的cookie。

  Django的封装做的很不错,这里只需要一句话就可以完成注销。

# 注销
def logout(request):
    request.session.clear()
    return redirect('/login')

  我们再把写好的注销函数关联到模板layout.html中的注销链接中。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Olsen</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap-datetimepicker.min.css' %}">
    <style>
        .navbar{
            border-radius: 0;
        }
    </style>
</head>
<body>

<nav class="navbar navbar-default">
    <!--  <div class="container-fluid">-->
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Olsen用户管理系统</a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="/admin/list">管理员账户</a></li>
                <li><a href="/depart/list">部门管理</a></li>
                <li><a href="/user/list">用户管理</a></li>
                <li><a href="/num/list">靓号管理</a></li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">登录</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">ClarkHu <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人资料</a></li>
                        <li><a href="#">我的信息</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="/logout">注销</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div>
    {% block content %}
    {% endblock %}
</div>


<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>

  效果如下:

在这里插入图片描述

5. 用户信息

  在完成上一步的时候我偶然发现,我们之前模板中右上角的用户信息直接被写死了。在效果展示中我们可以看到,明明是Taylor登录的系统,但是左上角仍然显示ClarkHu。

在这里插入图片描述

  当然这也非常简单啊。之前我们都端往前端穿request或者是form,其实我们可以直接在html中使用request,还是拿模板文件layout.html示例。只需要把之前的下拉框代码:

<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
   aria-expanded="false">ClarkHu <span class="caret"></span></a>

  改成现在这样的:

<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
   aria-expanded="false">{{ request.session.info.username }} <span class="caret"></span></a>

  就可以完成这个效果:

在这里插入图片描述

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铖铖的花嫁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值