基于Django3.0 实现注册、登录

Django3.0 实现注册和登录功能

2020-6-1

Django3.0在2.0的基础上完善了websock的功能,因为想在我现在做的项目上加上及时通讯的功能,使用了Django3.0版本,本教程讲解如何实现网页登录功能。

源码:请访问Github
在这里插入图片描述

 

1、 创建APP

对于创建项目和虚拟环境我就不赘述了,直接从APP开始。为了代码管理更方便我把所有的APP放到APPS文件夹内,此时的目录结构如图

在这里插入图片描述
统一有关用户操作的功能都放到user的app中。
在setting.py中注册user APP
在这里插入图片描述
为了方便管理我在本项目中使用了二级域名。所以在mysite的url中还需要配置二级域名的url。所以需要在user APP中新增一个名为urls的python文件。并且在mysite的urls中添加进去。在这里插入图片描述
此时基础工作已经完成。

 

2、 需求分析&数据库设计

为了满足我们的需求,对页面进行分析。设计数据库。
注册功能中,通过邮箱注册,服务器发送注册链接到注册邮箱,用户点击链接完成注册。
所以需要的字段有:
用户名
用户密码
验证链接码等。django自带的库中有user相关的库,进行重写:

 
models.py

import datetime

from django.db import models

from django.contrib.auth.models import AbstractUser


# 用户信息
class UserInformationModel(AbstractUser):
    nike_name = models.CharField(max_length=50, verbose_name=u"昵称", default='')
    birthday = models.DateField(verbose_name=u'生日', null=True, blank=True)
    gender = models.CharField(max_length=6, choices=(("male", u"男"), ("female", u"女")), default="female")
    image = models.ImageField(upload_to="image/%Y/%m", default="image/default.png", max_length=200, null=True)
    describe = models.CharField(max_length=500, default=' ', verbose_name=u'个性签名')

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name

    def __unicode__(self):
        return self.username


# 验证码
class EmailVerificationModel(models.Model):
    code = models.CharField(max_length=20, verbose_name=u'验证码')
    email = models.EmailField(max_length=200, verbose_name=u'邮箱')
    send_type = models.CharField(max_length=10, choices=(("register", u'注册'), ("forget", u'密码找回')))
    send_time = models.DateTimeField(default=datetime.now)

    class Meta:
        verbose_name = u'邮箱验证码'
        verbose_name_plural = verbose_name

需要将以上的字段保存到数据库中,所以在setting中配置数据库信息。

DATABASES = {
    'default': {
    # 数据库类型
        'ENGINE': 'django.db.backends.mysql',
        # 数据库名称
        'NAME': 'blog',
        #登录用户
        'USER': 'root',
        #登录密码
        'PASSWORD': '151968',
        # 主机
        'HOST': 'localhost',
        #端口
        'PORT': '3306',
    }
}

在数据库同步之前,因为是继承AbstractUser类,需要在setting.py文件中添加:

# 设置用户组
AUTH_USER_MODEL = "user.UserInformationModel"

否则会报错。

数据库同步:

python manage.py makemigrations
python manage.py migrate

在数据库中发现下表表示同步成功

在这里插入图片描述

3、 功能实现

使用Django自带的Form进行简单的前端验证,以减少垃圾注册,减少服务器负担。
在user文件夹中新建一个form.py

form.py:

from django import forms

from captcha.fields import CaptchaField

#用户登录
class UserLoginForm(forms.Form):
    Username = forms.CharField(required=True)
    Password = forms.CharField(required=True, min_length=6)

#用户注册
class UserRegisterForm(forms.Form):
	# 用户名验证 字段需要与前端input的name属性保持一致
    Username = forms.CharField(required=True)
    # 密码验证 字段需要与前端input的name属性保持一致
    Password = forms.CharField(required=True, min_length=6)
    # 重复密码验证  字段需要与前端input的name属性保持一致
    rPassword = forms.CharField(required=True)
    # 验证码
    captcha = CaptchaField(error_messages={"Invalid": u'验证码错误'})

验证码使用第三方库captcha,pip安装后注册到setting.py中.

3.1 邮箱验证码功能实现

在user app中添加一个python package文件夹,添加Email_Send.py
此时目录结构为:
在这里插入图片描述

在Email_Send.py中编写:

from apps.user.models import EmailVerificationModel

from random import Random

from django.core.mail import send_mail

from mysite.settings import EMAIL_FROM


# 获取随机字符串
def get_random_string(code_length):
    string = ""
    chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(code_length):
        string += chars[random.randint(0, length)]
    return string


def Email_Send_Register(email,sendtype="register"):
    email_content = EmailVerificationModel()
    email_code = get_random_string(16)
    email_content.code = email_code
    email_content.email = email
    email_content.send_type = sendtype
    email_content.save()

    email_title = '用户注册'
    email_body = '点击此链接完成注册.http://127.0.0.1:8000/user/sign-up/activate/{0}'.format(email_code)
    send_key = send_mail(email_title, email_body, EMAIL_FROM, [email])


def Email_Send_Forget(email,sendtype="forget"):
    email_content = EmailVerificationModel()
    email_code = get_random_string(16)
    email_content.code = email_code
    email_content.email = email
    email_content.send_type = sendtype
    email_content.save()

    email_title = '密码找回'
    email_body = '点击此链接密码找回.http://127.0.0.1:8000/user/forget/verification/{0}'.format(email_code)
    send_key = send_mail(email_title, email_body, EMAIL_FROM, [email])

同时并且在setting中添加邮箱配置:

# 邮件发送,使用第三方代理服务器发送
EMAIL_HOST = 'smtp.qq.com'

EMAIL_PORT = 25

# 邮件发送主机用户名
EMAIL_HOST_USER = 'XXXX@qq.com'
# SMTP代理发送密码
EMAIL_HOST_PASSWORD = ''

EMAIL_USE_TLS = False

# 代理服务器发送
# EMAIL_USE_SSL = True
# 邮件发送者地址
EMAIL_FROM = 'XXXX@qq.com'

因为注册页面需要使用到验证码,所以需要对验证码进行配置:

# captcha验证码
CAPTCHA_OUTPUT_FORMAT = '%(image)s %(text_field)s %(hidden_field)s '
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_null',)
CAPTCHA_LENGTH = 6
# 超时(minutes)
CAPTCHA_TIMEOUT = 1

静态文件配置:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

配置之后再同步一下数据库:

python manage.py makemigrations
python manage.py migrate

此时就可以编写视图函数了。
 

3.2、 View.py编写

views.py:

from django.shortcuts import render

# emil
from apps.user.until.Email_Send import *

# 加密
from django.contrib.auth.hashers import make_password
from django.contrib.auth.hashers import check_password

# 查询
from django.db.models import Q
from django.contrib.auth.backends import ModelBackend

# 登录
from django.contrib.auth import authenticate
from django.contrib.auth import login
from django.contrib.auth import logout

# 跳转
from django.http import HttpResponseRedirect
from django.http import HttpResponse
from django.shortcuts import reverse

# view
from django.views.generic.base import View

# from
from .form import *


# model
from .models import UserInformationModel


# 重写类方法根据邮箱和用户名查找
class UserVerificationView(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):

        try:
            user_information = UserInformationModel.objects.get(Q(email=username) | Q(username=username))
            if check_password(password, user_information.password):
                return user_information
        except Exception as err:
            return None


# 登录
class UserLoginView(View):

    def get(self, request):
        user_login_form = UserLoginForm()
        return render(request, "sign-in.html", locals())

    def post(self, request):
        user_login_form = UserLoginForm(request.POST)
        if user_login_form.is_valid():
            username = request.POST.get("Username", "")

            password = request.POST.get("Password", "")

            try:
                user_information = authenticate(username=username, password=password)
                if user_information is not None:

                    if user_information.is_active:
                        login(request, user_information)
                        # 定向跳转到博客主页
                        return HttpResponseRedirect(reverse("blog-index"))

                    else:
                        return render(request, "sign-in.html", {
                            "message": "该用户尚未激活!",
                        })
                else:
                    return render(request, "sign-in.html", {
                        "message": "用户名或密码错误!",
                    })

            except Exception as error:
                return render(request, "sign-in.html", {
                    "message": "未知错误,请与网站管理员联系!",
                })


# 注册
class UserRegisterView(View):
    def get(self, request):
        UserRegister = UserRegisterForm()
        return render(request, "sign-up.html", locals())

    def post(self, request):
        UserRegister = UserRegisterForm(request.POST)
        if UserRegister.is_valid():
            username = request.POST.get("Username", "")
            password = request.POST.get("Password", "")
            rpassword = request.POST.get("rPassword", "")
            if password != rpassword:
                return render(request, "sign-up.html", {
                    "message": "两次密码输入不一致!",
                    "UserRegister": UserRegister,
                })

            if UserInformationModel.objects.filter(email=username):
                return render(request, "sign-up.html", {
                    "message": "该邮箱已被注册!",
                    "UserRegister": UserRegister,
                })

            UserInformation = UserInformationModel()
            UserInformation.email = username
            UserInformation.username = username
            UserInformation.is_active = False
            UserInformation.password = make_password(password)
            UserInformation.save()

            Email_Send_Register(email=username, sendtype="register")

            return render(request, "user-sign-up-turn.html")

        else:
            UserRegister = UserRegisterForm()
            return render(request, "sign-up.html", locals())


# 用户验证
class UserActivateView(View):
    def get(self, request, activate_code):
        all_records = EmailVerificationModel.objects.filter(code=activate_code)
        if all_records:
            for record in all_records:
                email = record.email
                user = UserInformationModel.objects.get(email=email)
                user.is_active = True
                user.save()
        else:
            return render(request, 'user-sign-up-activate-fail.html')
        return render(request, 'user-sign-up-activate-succeed.html')

user-sign-in.html:

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <title>用 户 登 录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="application/x-javascript"> addEventListener("load", function () {
        setTimeout(hideURLbar, 0);
    }, false);

    function hideURLbar() {
        window.scrollTo(0, 1);
    } </script>

    <link href="{% static 'login&register/css/snow.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <link href="{% static 'login&register/css/style.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <link rel="stylesheet" href="{% static 'font-awesome/css/font-awesome.css' %}">
    <!- Layui ->
    <link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
    <!- ico ->
    <link href="{% static "login&register/css/font-awesome.min.css" %}" rel="stylesheet" type="text/css" media="all">


    <!- js ->
    <script type="text/javascript" src="{% static 'jQuery/jquery-3.3.1.js' %}"></script>
    <script type="text/javascript" src="{% static 'jQuery/jquery-3.3.1.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'layui/layui.js' %}"></script>
    <script type="text/javascript" src="{% static 'layui/layui.all.js' %}"></script>


    <!-- web font -->
    <link href="//fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
    <link href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">

    <!-- //web font -->
</head>
<body>
<div class="snow-container">
    <div class="snow foreground"></div>
    <div class="snow foreground layered"></div>
    <div class="snow middleground"></div>
    <div class="snow middleground layered"></div>
    <div class="snow background"></div>
    <div class="snow background layered"></div>
</div>
<div class="top-buttons-agileinfo">
    <a href="#">登录</a>
    <a href="{% url 'register' %}" class="active">注册</a>
</div>
<h1 class="pad-bottom-1"> Sign In </h1>
<div class="main-agileits">
    <!--form-stars-here-->
    <div class="form-w3-agile">
        <h2 class="sub-agileits-w3layouts">Sign In</h2>
        <form action="{% url 'login' %}" method="post">

            <div class="input-group pos-relative">
                <span class="input-group-addon user-box"><i class="fa fa-envelope-o fa-fw"></i></span>
                <input id="input-control" type="text" name="Username" placeholder="用户名或邮箱" required=""/>
            </div>
            <div class="input-group pos-relative">
                <span class="input-group-addon user-box" style=""><i class="fa fa-lock fa-fw"></i></span>
                <input id="input-control" type="password" name="Password" placeholder="密码" required=""/>
            </div>

            <p class="p-bottom-w3ls left pad-bottom-3 pad-right-3">
                <a class="color-fff" href="{% url 'register' %}">点击注册</a>
            </p>
            <p class="p-bottom-w3ls left pad-bottom-3">
            </p>
            <div class="submit-w3l">
                <input type="submit" value="登录">
            </div>
            {% csrf_token %}
        </form>
    </div>
</div>
<!--//form-ends-here-->
<!-- copyright -->
<div class="copyright w3-agile">
    <p> © 2020 rainbowsea.xyz</p>
</div>
<!-- //copyright -->

<script type="text/javascript">

    //监听输入
    $('input[name=Username]').bind('input propertychange', function () {
        console.log($('input[name=Username]').val())
    });
    $('input[name=Password]').bind('input propertychange', function () {
        let password = $('input[name=Password]').val()
        if (password.length < 8) {
            layer.tips('您的密码长度不足8位,当前长度为:' + password.length, $('input[name=Password]'));
        } else {
            layer.close(layer.tips('您的密码长度不足8位,当前长度为:' + password.length, $('input[name=Password]')));
        }
    });

    $("input[type=submit]").click(function () {
        let password = $('input[name=Password]').val()
        if (password.length < 8) {
            alert("密码长度不够!")
            return false;
        }
    })

    var message = "{{ message }}"
    if (message){
        layer.msg(message)
    }
</script>


</body>
</html>

user-sign-up.html:

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <title>用 户 注 册 </title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="application/x-javascript"> addEventListener("load", function () {
        setTimeout(hideURLbar, 0);
    }, false);

    function hideURLbar() {
        window.scrollTo(0, 1);
    } </script>
    <link href="{% static 'login&register/css/snow.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <link href="{% static 'login&register/css/style.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <!- Layui ->
    <link href="{% static 'layui/css/layui.css' %}" rel="stylesheet">
    <!- ico ->
    <link href="{% static "font-awesome/css/font-awesome.min.css" %}" rel="stylesheet" type="text/css" media="all">


    <!- js ->
    <script type="text/javascript" src="{% static 'jQuery/jquery-3.3.1.js' %}"></script>
    <script type="text/javascript" src="{% static 'jQuery/jquery-3.3.1.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'layui/layui.js' %}"></script>
    <script type="text/javascript" src="{% static 'layui/layui.all.js' %}"></script>

    <!-- google font -->
    <link href="//fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
    <link href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">

    <!-- //web font -->
</head>
<body>
<div class="snow-container">
    <div class="snow foreground"></div>
    <div class="snow foreground layered"></div>
    <div class="snow middleground"></div>
    <div class="snow middleground layered"></div>
    <div class="snow background"></div>
    <div class="snow background layered"></div>
</div>

<div class="top-buttons-agileinfo">
    <a href="{% url 'login' %}" class="active">登录</a><a href="#">注册</a>
</div>
<h1 class="pad-bottom-1"> Sign Up Form </h1>
<div class="main-agileits">
    <!--form-stars-here-->
    <div class="form-w3-agile">
        <h2 class="sub-agileits-w3layouts">登录</h2>
        <form action="{% url 'register' %}" method="post">
            {% block input %}
            <div class="input-group pos-relative">
                <span class="input-group-addon ico-box-register"><i class="fa fa-envelope-o fa-fw"></i></span>
                <input id="input-control" type="email" name="Username" placeholder="邮箱" required=""/>
            </div>
            <div class="input-group pos-relative">
                <span class="input-group-addon ico-box-register" style=""><i class="fa fa-lock fa-fw"></i></span>
                <input id="input-control" type="password" name="Password" placeholder="密码" required=""/>
            </div>
            <div class="input-group pos-relative">
                <span class="input-group-addon ico-box-register" style=""><i class="fa fa-lock fa-fw"></i></span>
                <input id="input-control" type="password" name="rPassword" placeholder="重复密码" required=""/>
            </div>

            <!-验证码 ->
            {{ UserRegister.captcha }}
            <!- 提交 ->
            <div class="submit-w3l">
                <input type="submit" value="Sign up">
            </div>
            {% csrf_token %}
            {% endblock %}
        </form>
    </div>
</div>
<!--//form-ends-here-->
<!- copyright ->
<div class="copyright w3-agile">
    <p> © 2020 rainbowsea.xyz</p>
</div>
<script>
    var msg = '{{ UserRegister.errors.captcha }}'
    if (msg) {
        layer.msg(msg)
    }

    /* 刷新验证码 */
    $('.captcha').click(function () {
        $.getJSON("/captcha/refresh/", function (result) {
            $('.captcha').attr('src', result['image_url']);
            $('#id_captcha_0').val(result['key'])
        });
    });
    $("#id_captcha_1").attr("placeholder", "请输入验证码")

    /*
    行为验证
     */
    $('input[name=Password]').bind('input propertychange', function () {
        let password = $('input[name=Password]').val()
        if (password.length < 8) {
            layer.tips('您的密码长度不足8位,且至少应是数字、字母、等两种字符以上的组合', $('input[name=Password]'));
        } else {
            layer.close(layer.tips('您的密码长度不足8位,且至少应是数字、字母、等两种字符以上的组合', $('input[name=Password]')));
        }
    });

    $('input[name=rPassword]').bind('input propertychange', function () {
        let password1 = $('input[name=Password]').val()
        let password2 = $('input[name=rPassword]').val()
        if (password1 != password2) {
            layer.tips("您两次输入的密码不一致", $('input[name=rPassword]'));
        } else {
            layer.close(layer.tips("您两次输入的密码不一致", $('input[name=rPassword]')));
        }
    });

    $('input[name=captcha_1]').bind('input propertychange', function () {
        if ($('input[name=captcha_1]').val().length < 6) {
            layer.tips("验证码长度小于6,当前长度为" + $('input[name=captcha_1]').val().length, $('input[name=captcha_1]'), {
                tips: 3
            })
        }
        else {
            layer.close(layer.tips("验证码长度小于6,当前长度为" + $('input[name=captcha_1]').val().length, $('input[name=captcha_1]'), {
                tips: 3
            }))
        }
    });

    $('input[type=submit]').click(function () {
        let password1 = $('input[name=Password]').val()
        let password2 = $('input[name=rPassword]').val()
        let regNumber = /\d+/; //验证0-9的任意数字最少出现1次。
        let regString = /[a-zA-Z]+/; //验证大小写26个字母任意字母最少出现1次。
        let code = $('input[name=captcha_1]').val()

        if (password1 != password2) {
            layer.msg('两次密码输入不一致');
            return false;
        }
        if (password1.length < 8) {
            layer.msg('密码长度小于8');
            return false
        }
        if (code.length < 6) {
            layer.msg('验证码错误');
            return false;
        }
        if (regNumber.test(password1) && regString.test(password1)) {
            return true
        } else {
            layer.msg('密码至少应是数字和字母的两种组合');
            return false;
        }
    });
    var message = "{{ message }}"
    if (message){
        layer.msg('{{message}}');
    }
</script>
</body>
</html>
4、注册路由

先配置user下面的路由
user/urls.py:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

'''
@File    :   urls.py    
@Time :  2020/6/1 14:23
@Author :   c-cc
@Software :  PyCharm
'''


from django.urls import path

from .views import *

urlpatterns = [
    path('sign-in/', UserLoginView.as_view(), name="login"),

    path('sign-up/', UserRegisterView.as_view(), name="register"),

    # 活跃验证
    path('sign-up/activate/<str:activate_code>/', UserActivateView.as_view(), name="activate"),

    # 退出登录
    path('logout/', UserLogoutView.as_view(), name="logout"),
]

mysite/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('user/', include('apps.user.urls')),

    # capthca
    path('captcha/', include('captcha.urls')),
]

自此完成Django的登录和注册功能。我自己初学的时候也是遇到了很多的坑,现在写一比较详细的教程来帮助一下新手玩家不踩我踩过的坑。还有很多功能还没完成,如果有时间我也会继续更新。另外此项目我也部署到了我的个人服务器上附上地址::http://rainbowsea.xyz/,当然主页是我网上找的,自己写还是很恼火的,除此以外的大多都是我自己操作的,也、欢迎各位大佬莅临参观。如果发现了BUG,别告诉我,最近忙毕业设计…

转载请注明来源。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值