11、Django邮箱||验证码||登录

创建 django 项目, APP 略.....

准备工作

1. Model的编写

model.py

from django.db import models

gender = (
        ('男','男'),
        ('女','女'),
    )

class Before_User(models.Model):
    name = models.CharField(max_length=128, null=False, verbose_name='用户名')
    password = models.CharField(max_length=256, null=False, verbose_name='密码')
    email = models.EmailField(unique=True, null=False, verbose_name='邮箱')
    sex = models.CharField(max_length=32, choices=gender, default='男', verbose_name='性别')
    is_active = models.BooleanField(default=False)
    c_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('-c_time',)
        db_table = 'bf_user'
        verbose_name = '前端注册用户'
        verbose_name_plural = verbose_name

2.第三方库安装

pipenv install django--simple-captcha
pipenv install python-memcached 

3. 注册APP

admin.py 注册

from django.contrib import admin
from .models import Before_User

admin.site.register(Before_User)

4. 生成数据库

python manage.py makemigrations
python manage.py migrate

4.django 后台创建一个注册用户(略)

setting.py

INSTALLED_APPS = [
    '''略'''
    'Apply_users', # 前端注册用户
    'captcha',

]
# 发送邮箱验证码
EMAIL_HOST = "smtp.163.com"     # 服务器
EMAIL_PORT = 25                 # 一般情况下都为25
EMAIL_HOST_USER = "xxxxx@163.com"     # 账号
EMAIL_HOST_PASSWORD = "xxxxx"          # 密码 (注意:这里的密码指的是授权码)
EMAIL_USE_TLS = False       # 一般都为False
EMAIL_FROM = "xxxxx@163.com"      # 邮箱来自


# 定义memcache服务器连接
import memcache
CACHES = {
    'default':{
        'BACKEND':'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '10.10.181.18:11211',
        ],
        'KEY_FUNCTION': lambda key,prefix_key,version:"django:%s"%key   #自定义存储格式
    }
}

url.py 路径信息

from django.urls import path, re_path
from . import views

urlpatterns = [
    path('index/', views.index, name='bf_index'),
    path('login/', views.LoginView.as_view(), name='bf_login'),
    path('register/', views.RegisterView.as_view(), name='bf_register'),
    path('logout', views.logout, name='bf_logout'),
    path('email_code/', views.email_code, name='bf_email_code'),             # 发送邮箱验证码
    re_path('forgetpwd/$', views.ForgetPwdView.as_view(), name='bf_forgetpwd'),      # 忘记密码,验证邮件
]

forms.py 验证

from django import forms
# from django.core import validators
from .models import Before_User, gender
from captcha.fields import CaptchaField
from django.contrib.auth.hashers import make_password, check_password



# 自定义 base 错误类
class BaseForm(forms.Form):
    def get_errors(self):
        errors = self.errors.get_json_data()
        new_errors = {}
        for key,message_dicts in errors.items():
            messages = []
            for message_dict in message_dicts:
                message = message_dict['message']
                messages.append(message)
                new_errors[key] = messages
        return new_errors


# 登录form验证
class LoginViewForm(BaseForm):
    '''
    登录Form
    attrs:可自定义html属性
    '''
    email = forms.EmailField(label='邮箱', widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label='密码', max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='验证码', error_messages={'invalid': '验证码错误'})

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        password = cleaned_data.get('password')
        user = Before_User.objects.filter(email=email).first()
        if not user.is_active:
            raise forms.ValidationError(message='你的账户已被禁止登陆,请联系管理员')
        if user and check_password(password, user.password):
            cleaned_data.update({'id': user.id, 'name': user.name})
            return cleaned_data
        raise forms.ValidationError(message='邮箱或者密码错误!')



# 注册form验证
class RegisterForm(BaseForm):
    '''注册Form'''
    name = forms.CharField(max_length=128, label='用户名', widget=forms.TextInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=forms.EmailInput(attrs={'class': 'form-control'}))
    sex = forms.ChoiceField(label='性别', choices=gender)
    password = forms.CharField(label='密码', max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label='确认密码', max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='验证码')
    code = forms.CharField(label='邮箱验证码', max_length=50, widget=forms.TextInput(attrs={'class': 'form-control'}))

    def clean_email(self):
        email = self.cleaned_data.get('email')
        exists = Before_User.objects.filter(email=email).exists()
        if exists:
            raise forms.ValidationError(message='邮箱已被注册')
        return email

    def clean_password(self):
        password = self.cleaned_data.get('password')
        if len(password) < 6:
            raise forms.ValidationError("密码长度小于6位")
        elif len(password) > 20:
            raise forms.ValidationError('密码长度大于20位')
        return password

    def clean_password2(self):
        password = self.cleaned_data.get('password')
        password2 = self.cleaned_data.get('password2')
        if password != password2:
            raise forms.ValidationError(message='两次密码输入不一致')
        return password2


    def clean_code(self):
        from django.core.cache import cache
        code = self.cleaned_data.get('code')
        email = self.cleaned_data.get('email')
        cache_code = cache.get(email)
        if cache_code != code:
            raise forms.ValidationError(message='邮箱验证码错误')
        return code



# 修改密码 form 验证

class ForgetPwdForm(BaseForm):
    email = forms.EmailField(label='邮箱', widget=forms.EmailInput(attrs={'class': 'form-control'}))
    code = forms.CharField(label='验证码', widget=forms.TextInput(attrs={'class': 'form-control'}))

    def clean_email(self):
        email = self.cleaned_data.get('email')
        exists = Before_User.objects.filter(email=email).exists()
        if exists:
            return email
        raise forms.ValidationError('无此邮箱用户,请检查你的邮箱是否有误')

    def clean_code(self):
        from django.core.cache import cache
        code = self.cleaned_data.get('code')
        db_code = cache.get(self.cleaned_data.get('email'))
        if code != db_code:
            raise forms.ValidationError('邮箱验证码错误')
        return code

send_mail.py

from random import Random  # 用于生成随机码
from django.core.mail import send_mail  # 发送邮件模块
from django.conf import settings    # setting.py添加的的配置信息
from django.core.cache import cache
import smtplib
import logging
from django.contrib.auth.hashers import make_password
from .models import Before_User

logger = logging.getLogger(__name__)


# 生成随机字符串
def random_str(randomlength=4):
    """
    随机字符串
    :param randomlength: 字符串长度
    :return: String 类型字符串
    """
    str = ""
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        str += chars[random.randint(0, length)]
    return str


# 发送电子邮件
def send_code_email(email):
    """
    发送电子邮件
    """
    code = random_str(4)
    cache.set(email, code, 120)
    email_title = "Django"
    email_body = "您的邮箱验证码为:{0}, 该验证码有效时间为两分钟,请及时进行验证。".format(code)
    try:
        send_mail(email_title, email_body, settings.EMAIL_FROM, [email])
        return {'code': 200, 'message': '邮箱发送成功,请查收'}
    except smtplib.SMTPRecipientsRefused:
        return {'code': 404, 'message': '未找到此邮箱'}
    except Exception as e:
        logger.error("系统邮件配置有误,请检查......")
        return {'code': 500, 'message': '服务内部错误,请稍等,着急请联系管理员......'}



# 忘记密码发送修改过的密码给用户的邮件
def send_accout_email(email):
    email_title = "Django密码修改:"
    new_pwd = random_str(16)
    email_body = "邮箱:  {0}\n 密码:  {1}\n 这是系统随机设置密码,请及时进行修改。".format(email, new_pwd)
    try:
        send_mail(email_title, email_body, settings.EMAIL_FROM, [email])
        mf_user = Before_User.objects.get(email=email)
        mf_user.password = make_password(new_pwd)
        mf_user.save()
        return {'code': 200, 'message': '密码修改信息已发送到你邮箱,请查收'}
    except smtplib.SMTPRecipientsRefused:
        return {'code': 404, 'message': '未找到此邮箱'}
    except Exception as e:
        logger.error("系统邮件配置有误,请检查......")
        return {'code': 500, 'message': '服务内部错误,请稍等,或者请联系管理员......'}

views.py

from django.shortcuts import render, redirect,reverse
from django.views.generic import View
from django.http import JsonResponse
from .forms import LoginViewForm, RegisterForm, ForgetPwdForm
from .models import Before_User
from django.contrib.auth.hashers import make_password
from .send_email import send_code_email, send_accout_email


def index(request):
    return render(request, 'bf/bf_index.html')


# 注册使用
class RegisterView(View):
    def get(self, request):
        if request.session.get('is_login', None):
            return redirect(reverse("bf_index"))
        register_form = RegisterForm()
        return render(request, 'bf/bf_register.html',locals())

    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            name = register_form.cleaned_data.get('name')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')
            password = register_form.cleaned_data.get('password')
            new_user = Before_User.objects.create(name=name, email=email, sex=sex, password=make_password(password), is_active=True)
            new_user.save()
            return redirect(reverse('bf_login'))

        message = [item[0] for item in register_form.errors.values()]
        message = ','.join(message)
        return render(request, 'bf/bf_register.html', locals())


# 登录使用
class LoginView(View):
    def get(self, request):
        if request.session.get('is_login', None):
            return redirect(reverse("bf_index"))
        login_form = LoginViewForm()
        return render(request, 'bf/bf_login.html', locals())

    def post(self, request):
        login_form = LoginViewForm(request.POST)
        if login_form.is_valid():
            user_id = login_form.cleaned_data.get('id')
            user_name = login_form.cleaned_data.get('name')
            request.session['is_login'] = True
            request.session['user_id'] = user_id
            request.session['user_name'] = user_name
            return redirect(reverse('bf_index'))

        message = [item[0] for item in login_form.errors.values()]
        message = ','.join(message)
        return render(request, 'bf/bf_login.html', locals())



# 忘记密码视图并发送修改后的密码给用户
class ForgetPwdView(View):
    def get(self, request):
        FORGETPWD_FORM = ForgetPwdForm()
        return render(request, 'bf/bf_forgetpwd.html', locals())

    def post(self, request):
        FORGETPWD_FORM = ForgetPwdForm(request.POST)
        if FORGETPWD_FORM.is_valid():
            email = FORGETPWD_FORM.cleaned_data.get('email')
            send_accout_email(email=email)
            return redirect(reverse('bf_login'), locals())

        message = [item[0] for item in FORGETPWD_FORM.errors.values()]
        message = ','.join(message)
        return render(request, 'bf/bf_forgetpwd.html', locals())



# 登出使用
def logout(request):
    if not request.session.get('is_login', None):
        return redirect(reverse('bf_index'))
    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return render(request, 'bf/bf_index.html')



# 发送邮箱验证码 url 视图函数
def email_code(request, methods=['POST']):
    email = request.POST.get('email')
    data = send_code_email(email)
    return JsonResponse(data, safe=False)

摸版 templates

bf_base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %} {% endblock%}
    </title>
</head>
<body>

{% block content %} {% endblock %}



<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
        $(function () {
            // Add refresh button after field (this can be done in the template as well)
            // $('img.captcha').after(
            //     $('<a class="btn btn-sm text-white btn-info pull-right captcha-refresh">看不清</a>')
            // );
            // 点击图形验证码切换效果
            $('.captcha').click(function () {
                var $form = $(this).parents('form');
                var url = location.protocol + "//" + window.location.hostname + ":"
                    + location.port + "/captcha/refresh/";

                // Make the AJAX-call
                $.getJSON(url, {}, function (json) {
                    $form.find('input[name="captcha_0"]').val(json.key);
                    $form.find('img.captcha').attr('src', json.image_url);
                });
                return false;
            });
        });
</script>


<script>
    // 发送邮件,并倒计时
    $(function() {
        $('.code').click(function(event){
            event.preventDefault();
            var self = $(this);
            var email = $("input[name='email']").val();
            var csrfmiddlewaretoken = $("[name='csrfmiddlewaretoken']").val();
            $.post({
                'url': '{% url "bf_email_code" %}',
                'data': {
                    'email': email,
                    'csrfmiddlewaretoken': csrfmiddlewaretoken
                },
                success: function (data) {
                    if (data['code'] == 200) {
                        alert('邮箱验证码发送成功!');
                        self.attr("disabled", true);
                        var timeCount = 60;
                        var timer = setInterval(function () {
                            timeCount--;
                            self.attr(timeCount);
                            if (timeCount <= 0) {
                                self.removeAttr('disabled');
                                clearInterval(timer);
                                self.text('发送验证码');
                            }
                        }, 1000);
                    } else {
                        alert(data['message']);
                    }
                }
            })

        })
    })
</script>

</body>
</html>

bf_login.html

<div data-gb-custom-block data-tag="extends" data-0='bf/bf_base.html'></div>





<div data-gb-custom-block data-tag="block">


    登陆页面

</div>





<div data-gb-custom-block data-tag="block"></div>
<form action="<div data-gb-custom-block data-tag="url" data-0='bf_login'></div>" method="post">
    <div data-gb-custom-block data-tag="csrf_token"></div>

    <div data-gb-custom-block data-tag="if">
        {{ message }}
    </div>

<p>
    {{ login_form.email.label_tag }}
    {{ login_form.email }}
</p>


<p>
    {{ login_form.password.label_tag }}
    {{ login_form.password }}
</p>



<p>
      {{ login_form.ca{% extends 'bf/bf_base.html'%}


{% block title %}
    登陆页面
{% endblock %}


{% block content %}
<form action="{% url 'bf_login' %}" method="post">
    {% csrf_token %}

    {% if message %}
        {{ message }}
    {% endif %}

<p>
    {{ login_form.email.label_tag }}
    {{ login_form.email }}
</p>


<p>
    {{ login_form.password.label_tag }}
    {{ login_form.password }}
</p>



<p>
      {{ login_form.captcha.errors }}
      {{ login_form.captcha.label_tag }}
      {{ login_form.captcha }}
</p>

    <input type="submit" value="登录">
</form>

<a href="{% url 'bf_forgetpwd' %}">忘记密码?</a>
<a href="{% url 'bf_register' %}">注册</a>


{% endblock %}{% extends 'bf/bf_base.html'%}


{% block title %}
    登陆页面
{% endblock %}


{% block content %}
<form action="{% url 'bf_login' %}" method="post">
    {% csrf_token %}

    {% if message %}
        {{ message }}
    {% endif %}

<p>
    {{ login_form.email.label_tag }}
    {{ login_form.email }}
</p>


<p>
    {{ login_form.password.label_tag }}
    {{ login_form.password }}
</p>



<p>
      {{ login_form.captcha.errors }}
      {{ login_form.captcha.label_tag }}
      {{ login_form.captcha }}
</p>

    <input type="submit" value="登录">
</form>

<a href="{% url 'bf_forgetpwd' %}">忘记密码?</a>
<a href="{% url 'bf_register' %}">注册</a>


{% endblock %}

bf_register.html

{% extends 'bf/bf_base.html'%}


{% block title %}
    注册页面
{% endblock %}

{% block content %}

<form action="{% url 'bf_register' %}" method="post">

    {% csrf_token %}

    <!-- 显示全部错误信息 -->
    {% if message %}
        {{ message }}
    {% endif %}

    <p>
        {{ register_form.name.label_tag }}
        {{ register_form.name }}
    </p>

    <p>
        {{ register_form.email.label_tag }}
        {{ register_form.email }}
    </p>

    <p>
        {{ register_form.sex.label_tag }}
        {{ register_form.sex }}
    </p>

    <p>
        {{ register_form.password.label_tag }}
        {{ register_form.password }}
    </p>
    <p>
        {{ register_form.password2.label_tag }}
        {{ register_form.password2 }}
    </p>

    <p>
        {{ register_form.captcha.errors }}
        {{ register_form.captcha.label_tag }}
        {{ register_form.captcha }}
    </p>
    <p>
        {{  register_form.code.label_tag }}
        {{  register_form.code }}
        <!-- 只显示指定 form 验证错误信息-->
        {% if register_form.errors.code %}
            <span>{{ register_form.errors.email_captcha }}</span>
        {% endif %}
        <input type="submit" value="点击发送" class="code">
    </p>
    <input type="reset" value="重置">
    <input type="submit" value="注册">
</form>

{% endblock %}

bf_forget.html

{% extends 'bf/bf_base.html'%}


{% block title %}
    忘记密码
{% endblock %}

{% block content %}
<form action="{% url 'bf_forgetpwd' %}" method="post">

    {% csrf_token %}

    {% if message %}
        {{ message }}
    {% endif %}

<p>
    {{  FORGETPWD_FORM.email.label_tag }}
    {{  FORGETPWD_FORM.email }}
</p>
<p>
    {{ FORGETPWD_FORM.code.label_tag }}
    {{  FORGETPWD_FORM.code }}
    <input type="submit" value="点击发送" class="code">
</p>

    <input type="submit" value="提交">
</form>
{% endblock %}

bf_index.html

{% extends 'bf/bf_base.html'%}


{% block title %}
    首页
{% endblock %}




{% block content %}
    {% if request.session.is_login %}
        welcome {{ request.session.user_name }}!
    {% else %}
        未登录
    {% endif %}
{% endblock %}

登录+验证码

注册+验证码

忘记密码验证

Last updated