Django-可重用注册登录系统--项目搭建

news2025/2/1 6:55:20

文章目录

  • 一、项目开始前的思考
  • 二、搭建项目环境
  • 三、设计数据库模型
      • 数据库模型文件
      • 设置数据库后端
      • 注册app
      • 生成迁移脚本并写入数据库
      • 测试是否成功
      • 数据库模型后台管理
    • 路由与视图函数框架搭建
      • 路由配置
      • 视图函数的配置
      • 模板template的配置
      • 测试是否成功
    • 前端界面设计与优化
    • 完善登录的视图函数
    • session会话与登录的视图函数
    • 图片验证码
      • Django表单
        • 创建表单模型
        • 视图逻辑优化
        • Template页面优化
        • 验证是否正确
    • 邮箱注册
        • 发送邮件功能测试
        • 基本的注册功能实现
          • 注册表单
          • 实现注册视图
          • Template模板的更改
          • 测试是否成功
        • 注册添加密码加密功能
        • 邮件注册确认
          • 创建模型
          • 修改视图
          • 处理邮件确认请求
          • 修改登录规则
    • 其他

前言


项目最终代码: https://bitbucket.org/lvah/loginregister

一、项目开始前的思考

思考:注册(邮箱注册,手机,微信,QQ)、登录、注销

  • 路由配置

    urlpattern = [
        path('/register/', views.register), 
        path('/login/', views.login), 
        path('/logout/', views.logout), 
    ]
    
  • 视图配置(重点)----

  • 数据库模型Model:

    class User:
        id, name, password, email, create_time, update_time
        last_time(最后一次登录的时间), gender, province
    
  • 模板Template: register.html, login.html, index.html

二、搭建项目环境

  • 创建Django项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3StaWFJy-1686377086681)(可重用注册登录系统.assets/image-20210306092845721.png)]

  • 创建app

    $ python manage.py startapp login
    
  • 设置时区和语言

    # filename: loginRegister/settings.py
    # LANGUAGE_CODE = 'en-us'
    LANGUAGE_CODE = 'zh-hans'
    
    # TIME_ZONE = 'UTC'
    TIME_ZONE = 'Asia/Shanghai'
    
  • 数据库表生成

    $ python manage.py migrate           # 将迁移脚本的内容写入数据库并创建数据库表
    $ python manage.py  createsuperuser  # 创建后台登录的超级用户
    
  • 启动开发服务器

    • 方法一: 命令启动

      $  python manage.py runserver 9999   
      
    • 方法二: 配置Django Server

在这里插入图片描述

  • 浏览器访问,检测是否成功?(第一步完美搞定)

    • 访问网址: http://127.0.0.1:9999/
    • 访问网址: http://127.0.0.1:9999/admin/
  • git提交项目代码到本地仓库

git安装
git廖雪锋:https://www.liaoxuefeng.com/wiki/896043488029600/896827951938304
Git下载安装及环境配置,解决安装包下载慢问题(借鉴:顾七a,如有侵权,联系删除):https://blog.csdn.net/mengxiang_/article/details/128193219

$ git init
# 安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore
$ git add *  # 添加修改到暂存区
$ git commit -m "搭建项目开发环境"  # 将暂存区的代码提交到本地git仓库
$ git log  # 查看历史提交记录

三、设计数据库模型

作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户表User,在用户表里需要保存下面的信息:

  • 用户名(name): 必填,最长不超过128个字符且唯一(unique)
  • 密码(password): 必填,最长不超过256个字符
  • 邮箱地址(email): 使用Django内置的邮箱类型且唯一
  • 性别(gender): 性别, 使用choice,只能选择男或者女或者未知,默认为未知;
  • 创建时间(create_time): 用户创建时间
    • 注意点: auto_now_add=True时为添加时的时间,更新对象时不会有变动。
  • 修改时间(modify_time):用户最后一次修改时间
    • 注意点: auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。
  • 最后一次登录时间(last_login_time): 最后一次登录时间
    • ==注意点:==null=True的话,数据库中该字段是NULL,即允许空值
    • ==注意点:==blank=False(默认)的话,字段没被赋值则会抛错;和数据验证(表单验证等)有关.

数据库模型文件

# login/models.py

from django.db import models

# Create your models here.

# appname_siteuser
class SiteUser(models.Model):
    """用户的数据库模型,注册/登录需要"""
    gender_choice = (
        (0, "未知"),
        (1, "男"),
        (2, "女"),
    )
    name = models.CharField(max_length=128, unique=True, verbose_name="用户名")
    password = models.CharField(max_length=256, verbose_name="密码")
    email = models.EmailField(unique=True, verbose_name="电子邮箱")
    gender = models.IntegerField(choices=gender_choice, default=0, verbose_name="性别")
    # auto_now_add=True时为添加时的时间,更新对象时不会有变动。
    # auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    modify_time = models.DateTimeField(auto_now=True, verbose_name="最后一次修改时间")
    # null针对数据库层面的, blank针对表单的
    last_login_time = models.DateTimeField(null=True, blank=True,
                                           verbose_name="最后一次登录时间")
    def __str__(self):
        return  self.name

    class Meta:
        verbose_name = "网站用户管理"
        verbose_name_plural = verbose_name

设置数据库后端

Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改。

注册app

# loginRegister/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',  # 修改的内容
]

生成迁移脚本并写入数据库

$ python manage.py makemigrations
$ python manage.py migrate 

测试是否成功

打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功。

数据库模型后台管理

# login/admin.py
from django.contrib import admin
from login.models import SiteUser
# Register your models here.

# 后台管理设置的信息
class SiteUserAdmin(admin.ModelAdmin):
    list_display =  ['name', 'gender', 'email']
    list_display_links = ['name']
    list_filter = ['gender', 'create_time']
    list_per_page = 10

admin.site.register(SiteUser, SiteUserAdmin)

浏览器访问,检测是否成功?(完美搞定)

  • 访问网址: http://127.0.0.1:9999/admin/

路由与视图函数框架搭建

路由设计:

URL视图views模板功能
/index/login.views.indexindex.html首页
/login/login.views.loginlogin.html登录页面
/register/login.views.registerregister.html注册界面
/logout/login.views.logout无需返回页面登出界面

访问策略:

  • 未登录人员,不论是访问index还是login和logout,全部跳转到login界面
  • 已登录人员,访问login会自动跳转到index页面
  • 已登录人员,不允许直接访问register页面,需先logout
  • 登出后,自动跳转到login界面

路由配置

  • 主路由配置文件

    # loginRegister/urls.py
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('login.urls'))  # 添加的行, 如果没有前缀,访问子路由配置文件
    ]
    
  • 子路由配置文件(login子应用的)

    # login/urls.py(新建的文件)
    from django.urls import path, include
    from login import views
    
    urlpatterns = [
        path('index/', views.index, name='index'),
        path('login/', views.login, name='login'),
        path('register/', views.register, name='register'),
        path('logout/', views.logout, name='logout'),
    ]
    

视图函数的配置

# login/views.py
from django.shortcuts import render, redirect

# Create your views here.


def index(request):
    pass
    return render(request, 'login/index.html')

def login(request):
    pass
    return render(request, 'login/login.html')

def register(request):
    pass
    return render(request, 'login/register.html')

def logout(request):
    pass
    # redirect: 重定向(跳转)
    return redirect('/login/')

模板template的配置

  • templates/login/index.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>这是首页的模拟界面</h1>
</body>
</html>
  • templates/login/login.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form>
    用户名: <input type="text" placeholder="username"><br/>
    密码: <input type="password" placeholder="password"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>
  • templates/login/register.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册界面</title>
</head>
<body>
<h1>用户注册</h1>
<form>
    用户名: <input type="text" placeholder="username"><br/>
    电子邮箱: <input type="email" placeholder="email"><br/>
    密码: <input type="password" placeholder="password"><br/>
    确认密码: <input type="password" placeholder="password"><br/>
    <input type="submit" value="注册">
</form>
</body>
</html>

测试是否成功

浏览器访问,检测是否成功?(第一步完美搞定)

  • 访问网址: http://127.0.0.1:9999/index/
  • 访问网址: http://127.0.0.1:9999/login/
  • 访问网址: http://127.0.0.1:9999/register/

前端界面设计与优化

在颜值即正义的年代,但没有CSS和JS,样子真的令人无法接受。

然而,大多数使用Django的人都不具备多高的前端水平,通常也没有专业的前端工程师配合,自己写的CSS和JS却又往往惨不忍睹。怎么办?没关系,我们有现成的开源前端CSS框架!Bootstrap4就是最好的CSS框架之一!戳一下了解更多

Bootstrap核心汇总:

  • Bootstrap入门模板

  • Bootstrap栅格系统

  • Boostrap表单组件

  • Bootstrap警告框

完善登录的视图函数

  • html和视图函数交互的完善

    • 修改1. 有message信息则显示, 没有就不显示。
    • 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
    • 修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
    • 修改4:name="username"指定表单内容存储的key值名称, eg: {“username”:“你填的用户名”,“password”:“你填的密码” }
    # templates/login/login.html
    
    <div class="col-sm">
        <h3 style="text-align: center">用户登录</h3>
        # 修改1. 有message信息则显示, 没有就不显示。 
        {% if message %}
        <div class="alert alert-warning" role="alert">
            <strong>登录失败!</strong> {{ message }}
        </div>
        {% endif %}
    
        # 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
        <form action="/login/" method="post">
            # 修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
            {% csrf_token %}
            <div class="form-group">
                <label>用户名</label>
                # 修改4:name="username"指定表单内容存储的key值名称, eg: {"username":"你填的用户名"}
                <input type="text" class="form-control" name="username">
            </div>
            <div class="form-group">
                <label>Password</label>
                <input type="password" class="form-control" name="password">
                <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成</small>
            </div>
            <a href="/register/" class="text-success">
                <ins>新用户注册</ins>
            </a>
            <button type="submit" class="btn btn-primary float-right">登录</button>
        </form>
    
    </div>
    
  • 视图函数的完善

    # login/views.py
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username').strip()
            password = request.POST.get('password').strip()
            # print(username, password)
            if username and password:
                user = SiteUser.objects.filter(name=username, password=password).first()
                if user:
                    return  redirect('/index/')
                else:
                    message = "用户名或者密码错误"
                    return  render(request, 'login/login.html', {'message':message})
            else:
                message = "非法的数据信息"
                return render(request, 'login/login.html', {'message': message})
        return render(request, 'login/login.html')
    
    
  • 浏览器访问,检测是否成功?

    • 访问网址: http://127.0.0.1:9999/login/
    • 填写正确的用户名和密码/错误的用户名和密码测试是否为期待的效果。

session会话与登录的视图函数

  • 登录成功, 存储登录的用户信息到session中

    # login/views.py
    
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username').strip()
            password = request.POST.get('password').strip()
            # print(username, password)
            if username and password:
                user = SiteUser.objects.filter(name=username, password=password).first()
                if user:
                    # ------------核心修改的内容开始
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['username'] = user.name
                    # --------------核心修改的内容结束
                    return  redirect('/index/')
                else:
                    message = "用户名或者密码错误"
                    return  render(request, 'login/login.html', {'message':message})
            else:
                message = "非法的数据信息"
                return render(request, 'login/login.html', {'message': message})
        return render(request, 'login/login.html')
    
  • 登出时,清空session信息

    # login/views.py
    def logout(request):
        # 如果状态不是登录状态,则无法登出。
        if request.session.get('is_login'):
            request.session.flush()  # 清空session信息
        return  redirect('/login/')
    
  • 在首页添加登出的超链接并测试

    # templates/login/index.html
    # 核心代码如下:
        <h1>你好, {{ request.session.username }}, 这是首页的模拟界面</h1>
        <a href="/logout/"><strong style="font-size: 20px">登出</strong></a>
    
  • 浏览器访问,检测是否成功?

    • 访问网址: http://127.0.0.1:9999/index/

项目地址: https://gitee.com/half-summer/loginregister

图片验证码

为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。

在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(不必重复造轮子)。这个库叫做django-simple-captcha。

具体安装教程:戳我

Django表单

我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。

Django的表单给我们提供了下面三个主要功能:

  • 准备和重构数据用于页面渲染;
  • 为数据创建HTML表单元素;
  • 接收和处理用户从表单发送过来的数据。

编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表<form>中的一个<input>元素。

戳我了解更多Django表单操作

创建表单模型

# /login/forms.py(新建的文件)

from captcha.fields import CaptchaField
from django import  forms
class LoginForm(forms.Form):
    username = forms.CharField(label='用户名', required=True,
                               min_length=4, max_length=128)
    password = forms.CharField(label="密码", required=True,
                               min_length=4, max_length=10)
    captcha = CaptchaField(label="验证码")

视图逻辑优化

# login/views.py
def login(request):
    # 请求方法为POST提交
    if request.method == 'POST':
        # 修改1: 实例化表单对象
        login_form = LoginForm(request.POST)
        # 修改2: 验证表单数据的合法性
        if login_form.is_valid():
            # 修改3:获取表单填写的数据,数据清洗
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            user = SiteUser.objects.filter(name=username, password=password).first()
            if user:
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return  redirect('/index/')
            else:
                message = "用户名或者密码错误"
                # 修改4: locals()以字典方式返回当前所有的变量
                # eg:{'message':'xxxx', 'login_form':'xxx'}
                return  render(request, 'login/login.html', locals())
        else:
            message = "填写的登录信息不合法"
            return render(request, 'login/login.html', locals())
    # 请求方法是GET请求
    login_form = LoginForm()
    return render(request, 'login/login.html', locals())

Template页面优化

# templates/login/login.html(部分修改)

<h3 style="text-align: center">用户登录</h3>
# 修改1: 不同的报错,提示不同的信息
{% if login_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
    <strong>登录失败!</strong> 验证码不正确
</div>
{% elif message %}
<div class="alert alert-warning" role="alert">
    <strong>登录失败!</strong> {{ message }}
</div>
{% endif %}

<div class="form-group">
    # 修改2:
    <label>{{ login_form.username.label }}</label>
    <input type="text" class="form-control" name="username">
</div>
<div class="form-group">
    # 修改3:
    <label>{{ login_form.password.label }}</label>
    <input type="password" class="form-control" name="password">
    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
</div>

# 修改4: 最重要的,添加验证码表单
<div class="form-group">
    <label>{{ login_form.captcha.label }}</label>
    {{ login_form.captcha }}
</div>

验证是否正确

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOQnIXWd-1686377086683)(可重用注册登录系统.assets/image-20210307102108989.png)]

邮箱注册

发送邮件功能测试

  • 配置邮件信息

    # loginRegister/settings.py
    
    # mail configure(添加信息如下)
    EMAIL_HOST = 'smtp.163.com'  # 'smtp.qq.com'
    EMAIL_PORT = 25
    EMAIL_HOST_USER = 'ahalf_summer@163.com'   # 你的邮箱地址
    EMAIL_HOST_PASSWORD = 'NFPOJZLTRPJPANZE'   # 不是邮箱的登录密码,而是授权码(如何获取授权码)
    EMAIL_USE_SSL = False					   # 不开启ssl
    
  • 如何获取授权码?

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vN3uDaoo-1686377086684)(可重用注册登录系统.assets/image-20210307111227616.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Ad3Fp8y-1686377086684)(可重用注册登录系统.assets/image-20210307111400926.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JPfZ8SJb-1686377086684)(可重用注册登录系统.assets/image-20210307111451771.png)]

  • 交互式环境中测试发送邮件是否成功?

    Terminal输入命令> python manage.py shell
    In [1]: from django.core.mail import send_mail
    
    In [2]: from loginRegister.settings import EMAIL_HOST_USER
    
    In [3]: send_mail("测试邮件", "content", EMAIL_HOST_USER, ['ahalf_summer@163.com', 'ahalf_summer@126
       ...: .com'])
    
    
  • 验证是否成功? 查看是否受到邮件?

基本的注册功能实现

注册表单
# login/forms.py
class RegisterForm(forms.Form):
    username = forms.CharField(label="用户名", required=True, max_length=128)
    password1 = forms.CharField(label="密码", max_length=256, required=True)
    password2 = forms.CharField(label="确认密码", max_length=256, required=True)
    email = forms.EmailField(label="邮箱地址")
    captcha = CaptchaField(label='验证码')
实现注册视图
  • 如果用户已经登录,则不能注册跳转到首页。
  • 如果是GET请求,返回用户注册的html页面。
  • 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是否已经被注册, 将注册的信息存储到数据库,跳转到登录界面。
  • 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。
# login/views.py
def register(request):
    # 如果用户已经登录,则不能注册跳转到首页。
    if request.session.get('is_login', None):
        return redirect('/index/')
    # 如果是POST请求
    if request.method == 'POST':
        print(request.POST)
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        # 先验证提交的数据是否通过
        if register_form.is_valid():
            # 清洗数据
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')

            print(locals())
            # 接下来判断用户名和邮箱是否已经被注册
            same_name_user = SiteUser.objects.filter(name=username)
            print(same_name_user)
            if same_name_user:
                message = '用户名已经存在'
                return render(request, 'login/register.html', locals())
            same_email_user = SiteUser.objects.filter(email=email)
            if same_email_user:
                message = '该邮箱已经被注册了!'
                return render(request, 'login/register.html', locals())
            # 将注册的信息存储到数据库,跳转到登录界面
            new_user = SiteUser(name=username, password=password1, email=email)
            new_user.save()
            return  redirect('/login/')
    # 如果是GET请求,返回用户注册的html页面。
    register_form = RegisterForm()
    return render(request, 'login/register.html', locals()) 
Template模板的更改
# templates/login/register.html

<h3 style="text-align: center">用户注册</h3>
{% if register_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
    <strong>注册失败!</strong> 验证码不正确
</div>
{% elif message %}
<div class="alert alert-warning" role="alert">
    <strong>注册失败!</strong> {{ message }}
</div>
{% endif %}


<form action="/register/" method="post">
    {% csrf_token %}
    <div class="form-group">
        <label>{{ register_form.username.label }}</label>
        <input type="text" class="form-control" name="username">
    </div>
    <div class="form-group">
        <label>{{ register_form.email.label }}</label>
        <input type="email" class="form-control" name="email">
    </div>
    <div class="form-group">
        <label>{{ register_form.password1.label }}</label>
        <input type="password" class="form-control" name="password1">
        <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
    </div>
    <div class="form-group">
        <label>{{ register_form.password2.label }}</label>
        <input type="password" class="form-control" name="password2">
        <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
    </div>
    <div class="form-group">
        <label>{{ register_form.captcha.label }}</label>
        {{ register_form.captcha }}
    </div>
    <a href="/login/" class="text-success">
        <ins>用户登录</ins>
    </a>
    <button type="submit" class="btn btn-primary float-right">注册</button>
</form>
测试是否成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3uwP21aa-1686377086685)(可重用注册登录系统.assets/image-20210307122839587.png)]

注册添加密码加密功能

对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用。

  • login/utils.py中编写一个hash函数:

    def hash_code(s, salt='mysite'):# 加点盐
        h = hashlib.sha256()
        s += salt
        h.update(s.encode())  # update方法只接收bytes类型
        return h.hexdigest()
    
  • login/views.py中修改login和register视图

    def register(request):
        # .....省略部分代码
    	new_user = SiteUser(name=username, password=hash_code(password1), email=email)
        # .....省略部分代码
        
    def login(request):  
        # .....省略部分代码
    	user = SiteUser.objects.filter(name=username, 
                                       password=hash_code(password)).first()
        # .....省略部分代码
    

邮件注册确认

很自然地,我们会想到如果能用邮件确认的方式对新注册用户进行审查,既安全又正式,也是目前很多站点的做法。

创建模型

既然要区分通过和未通过邮件确认的用户,那么必须给用户添加一个是否进行过邮件确认的属性。

另外,我们要创建一张新表,用于保存用户的确认码以及注册提交的时间。

# /login/models.py

class SiteUser(models.Model):
    # .......
    has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")
    
    
class ConfirmString(models.Model):
    code = models.CharField(max_length=256, verbose_name="确认码")
    user = models.OneToOneField('SiteUser', on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    def __str__(self):
        return self.user.name + ":" + self.code

    class Meta:
        ordering = ["-create_time"]
        verbose_name = "确认码"
        verbose_name_plural = "确认码"

数据库模型更改,一定要生成迁移脚本和写入数据库。

python manage.py makemigrations
python manage.py migrate

顺便修改一下admin.py文件,方便我们在后台修改和观察数据

# login/admin.py
admin.site.register(ConfirmString)
修改视图
def register(request):
    # ................
    code = make_confirm_string(new_user)
    send_email(email, code)
    message = '请前往邮箱进行确认!'
    # ..................

make_confirm_string()是创建确认码对象的方法,代码如下:

import datetime

def make_confirm_string(user):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    models.ConfirmString.objects.create(code=code, user=user,)
    return code

send_email(email, code)方法接收两个参数,分别是注册的邮箱和前面生成的哈希值,代码如下:

def send_email(email, code):
    print('send mail.........')
    subject = '注册确认邮件'
    text_content = '''感谢注册,这里是登录注册系统网站!\
                    如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
    html_content = '''
    <p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>点击验证</a>\
    这里是登录注册系统网站!</p>
    <p>请点击站点链接完成注册确认!</p>
    <p>此链接有效期为{}天!</p>
    '''.format('127.0.0.1:9999', code, settings.CONFIRM_DAYS)

    send_mail(subject, text_content,
              settings.EMAIL_HOST_USER, [email, ], html_message=html_content)

最后的有效期天数为设置在settings中的CONFIRM_DAYS。下面是邮件相关的settings配置:

# 注册有效期天数
CONFIRM_DAYS = 3

测试:注册一个用户,判断是否能收到确认邮件。

处理邮件确认请求

在login子应用的urls.py中添加一条url:

path('confirm/', views.user_confirm,name='confirm'),

其次,在login/views.py中添加一个user_confirm视图。

  • 获取确认码信息
  • 数据库中是否有该确认码,如果没有, 返回说是无效的请求
  • 数据库中是否有该确认码,如果有, 判断是否过期? 如果过期,删除用户信息,否则更新用户信息。
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求!'
        return render(request, 'login/confirm.html', locals())

    create_time = confirm.create_time
    now = datetime.now()
    print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS))
    if now > create_time + timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期!请重新注册!'
    else:
        confirm.user.is_confirmed = True
        confirm.user.save()
        confirm.delete()
        message = '感谢确认,请使用账户登录!'
    return render(request, 'login/confirm.html', locals())

需要一个confirm.html页面,我们将它创建在/login/templates/login/下面:

页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化。

<h1 style="margin-left: 100px;">{{ message }}</h1>

<script>
window.setTimeout("window.location='/login/'",2000);
</script>
修改登录规则

既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:

if not user.has_confirmed:
    message = '该用户还未经过邮件确认!'
    return render(request, 'login/login.html', locals())

其他

  • 如何在windows安装redis? 点击进入

  • 理解Celery工作原理点击进入

    (实现异步任务和定时任务)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C/C++】函数参数默认值

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

【python+requests】接口自动化测试

这两天一直在找直接用python做接口自动化的方法&#xff0c;在网上也搜了一些博客参考&#xff0c;今天自己动手试了一下。 一、整体结构 上图是项目的目录结构&#xff0c;下面主要介绍下每个目录的作用。 Common:公共方法:主要放置公共的操作的类&#xff0c;比如数据库sql…

VPN(Virtual privacte network)浅谈

文章目录 VPN概念VPN类型站点-站点VPN客户端-站点VPN VPN的工作原理VPN职责职责一&#xff1a;保密完整性认证PSK算法实现&#xff08;献给大佬&#xff09;PSK应用演示RSA算法实现&#xff08;献给大佬&#xff09;RSA应用演示&#xff1a;实现签名 VPN两大框架VPN的误解VPN合…

Java调用Pytorch实现以图搜图(附源码)

Java调用Pytorch实现以图搜图 设计技术栈&#xff1a; 1、ElasticSearch环境&#xff1b; 2、Python运行环境&#xff08;如果事先没有pytorch模型时&#xff0c;可以用python脚本创建模型&#xff09;&#xff1b; 1、运行效果 2、创建模型&#xff08;有则可以跳过&#xf…

经典目标检测YOLO系列(1)YOLO-V1算法及其在VOC2007数据集上的应用

经典目标检测YOLO系列(1)YOLO-V1算法及其在VOC2007数据集上的应用 1 YOLO-V1的简述 1.1 目标检测概述 ​ 目标检测有非常广泛的应用&#xff0c; 例如&#xff1a;在安防监控、手机支付中的人脸检测&#xff1b;在智慧交通&#xff0c;自动驾驶中的车辆检测&#xff1b;在智…

TCP与UDP的可靠性传输

目录 一、TCP可靠性传输1、重传机制1.1、超时重传1.2、快速重传1.3、SACK1.4、Duplicate SACK 2、滑动窗口3、流量控制3.1 滑动窗口与流量控制3.2窗口关闭 4、拥塞控制4.1拥塞窗口4.2 慢启动4.3 拥塞避免4.4 拥塞发生4.5 快速恢复 二、UDP可靠性传输1、主要策略2、重传机制2.1 …

软件测试03:软件工程和软件生命周期

软件测试03&#xff1a;软件工程和软件生命周期 软件危机 软件危机是指落后的软件生产方式无法满足迅速增长的计算机软件需求&#xff0c;从而导致软件开发与维护过程中出现一系列严重问题的现象。 软件工程 基本软件危机对于计算机发展的阻碍&#xff0c;1968年&#xff0…

史上最详细的安装Kali-linux教程(附视频教程)

之前不少人问kali怎么安装&#xff0c;今天就发一篇利用VM虚拟机安装kali的详细教程&#xff0c;每一步都截图了&#xff0c;让大家尽可能的清楚每一步的操作。 1.2 使用 VM 虚拟机安装 Kali 1.2.1 官方下载 Kali Linux 官方网址&#xff1a;http://www.Kali.org 下载方式分…

跨域 —— 反向代理配置

跨域问题在讲Node.js学习中编写接口的时候就已经讲到了&#xff0c;由后端配置解决跨域问题&#xff0c;使用cors中间件解决跨域问题以及使用JSONP解决跨域&#xff08;仅支持GET请求&#xff09;&#xff0c;具体可以看一下这篇文章的内容&#xff1a;十二、Express接口编写 —…

python面向对象操作3(速通版)

目录 一、多态和类名 1.标准多态 2.实例属性和实例方法 3.类对象和类属性 4.对象保存 二、方法 1.类方法 3.四种方法的区别 三、模块 1.导入模块 2.自动模块导入 3.模块导入的几种形式 3.1模块导入的两种方式和别名 3.2 from 模块 import 成员 4.两种方法的区别…

【运筹优化】最短路算法之A星算法 + Java代码实现

文章目录 一、A星算法简介二、A星算法思想三、A星算法 java代码四、测试 一、A星算法简介 A*算法是一种静态路网中求解最短路径最有效的直接搜索方法&#xff0c;也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近&#xff0c;最终搜索速度越快。 二、A星算…

day52_Spring

今日内容 零、 复习昨日 一、Spring 零、 复习昨日 一、引言 以前 public class HelleServlet extends HttpServlet{UserService service new UsrServiceImpl();void doGet(){service.findUser();} }public interface UserService{User findUser(); } public class UserServ…

Tigase-Server 8.3.0在windows11下安装

一、JDK安装&#xff1a; tigase-server要求JDK 17,请先下载JDK17, 下载地址&#xff1a;https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe 配置环境变量&#xff1a;JAVA_HOME{JDK安装目录} 二、数据库安装&#xff1a;tigase-server8.3在windows下…

【算法系列 | 4】深入解析排序算法之——归并排序

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 决定开一个算法专栏&#xff0c;希望能帮助大…

Chrome内核插件开发报错:Unchecked runtime.lastError:的原因及解决办法。

本篇文章主要讲解,chrome内核插件开发时报错:Unchecked runtime.lastError: Extensions using event pages or Service Workers must pass an id parameter to chrome.contextMenus.create 的原因及解决办法。 日期:2023年6月10日 作者:任聪聪 报错现象: 查看报错路径,在…

项目经理必备!这四个高效管理工具帮你实现项目管理目标

在项目管理中&#xff0c;图形工具可以帮助我们让项目信息可视化&#xff0c;让项目管理更加高效&#xff0c;对于项目经理而言&#xff0c;这些工具都是好帮手。让我们一起看看&#xff0c;项目经理常用的管理工具都有那些吧~ 1&#xff0c;甘特图 甘特图是计划和管理项目的好…

【Spring使用注解更简单的实现Bean对象的存取】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、前言&#xff1a; 二、储存Bean对象和使…

天黑的时候如果下雨了,会比平常更亮一些

目录 一、最近的感受 二、自我的审视 三、如何变得强大 1.保持善良 2.不过度追求公平 3.在痛苦中找到自己的意义 4.令人振奋的生命力 四、情绪调节中的个人见解及如何处理情绪后的学习 1.运动 2.散步 3.找好朋友倾诉 五、总结 一、最近的感受 天黑的时候如果下雨了…

设计模式(十一):结构型之组合模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

C语言:写一个代码,使用 试除法 打印100~200之间的素数(质数)

题目&#xff1a; 使用 试除法 打印100~200之间的素数。 素数&#xff08;质数&#xff09;&#xff1a;一个数只能被写成一和本身的积。 如&#xff1a;7只能写成1*7&#xff0c;那就是素数&#xff08;质数&#xff09;了。 思路一&#xff1a;使用试除法 总体思路&#xff…