轻量级bug管理平台——登录

2.短信登录

  2.1展示页面(写路由、视图、模版)注:要往数据库里添加数据用ModelForm,例如注册,不用往数据库添加的话用Form更简洁。

  2.2点击发送短信

  2.3点击登录

 3、用户名/密码登录

3.1 python生成图片+写文字

https://www.cnblogs.com/wupeiqi/articles/5812291.html
pip  install pillow

3.2 Session&Cookie

3.3 页面显示‘

3.4 登录

 

2.1页面展示的代码:

新建url:

from django.urls import path
from web.views import account


urlpatterns = [
    path('register/', account.register, name='register'),#反向解析后得到:register
    path('login/sms/', account.login_sms, name='login_sms'), #短信登录的方法
    path('send/sms/', account.send_sms, name='send_sms'),
]

copy注册的页面展示模板,进行相应的修改:

{% extends 'layout/basic.html' %}
{% load static %}

{% block title %} 用户短信登录 {% endblock %}

{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">用户短信登录</div>
        <form id="smsForm" method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {#                    上述代码在html模板中隐去,在ModelForm中进行样式form-control的编写#}
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg"></span>
                            </div>
                            <div class="col-xs-5">
                                <input id="btnSms" type="button" class="btn btn-default" value="点击获取验证码">
                            </div>
                        </div>

                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {{ field }}
                        <span class="error-msg"></span>
                    </div>
                {% endif %}
            {% endfor %}
            <div class="row">
                <div class="col-xs-3">
                    <input id="btnSubmit" type="button" class="btn btn-primary" value="登 录"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block js %}
    <script>

    </script>
{% endblock %}

在forms中新建一个bootstrap,py文件。文件中的代码如下:

#面向对象的继承,在form和ModelForm中进行样式的设置
class BootStrapForm(object):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():     #循环字段进行样式的设置
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

在account,py中

from web.forms.bootstrap import BootStrapForm

class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_object:
            raise ValidationError('手机号不存在')
        return user_object

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')

        # 手机号不存在,则验证码无需再校验
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误,请重新输入')

        return code

 2.2点击发送短信

  2.3点击登录

 

完整的登录成功代码:(最简便的方式)

在login_sms.html中:

{% extends 'layout/basic.html' %}
{% load static %}

{% block title %} 用户短信登录 {% endblock %}

{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">用户短信登录</div>
        <form id="smsForm" method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {#                    上述代码在html模板中隐去,在ModelForm中进行样式form-control的编写#}
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg"></span>
                            </div>
                            <div class="col-xs-5">
                                <input id="btnSms" type="button" class="btn btn-default" value="点击获取验证码">
                            </div>
                        </div>

                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {{ field }}
                        <span class="error-msg"></span>
                    </div>
                {% endif %}
            {% endfor %}
            <div class="row">
                <div class="col-xs-3">
                    <input id="btnSubmit" type="button" class="btn btn-primary" value="登 录"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block js %}
    <script>
        //页面框架加载完成之后自动执行函数
        $(function () {
            bindClickBtnSms();
            bindClickSubmit();
        });

        /*
     点击登录
       */
        function bindClickSubmit() {
            $('#btnSubmit').click(function () {
                $('.error-msg').empty();
                //收集表单中的数据 $('#regForm').serialize()//所有字段数据+csrf token
                //数据ajax发送到后台
                $.ajax({
                    url: "{% url 'login_sms' %}",
                    type: "POST",
                    data: $('#smsForm').serialize(),//所有字段数据+ csrf token
                    dataType: "JSON",
                    success: function (res) {
                        if (res.status) {
                            {#跳转页面#}
                            location.href = res.data;
                        } else {
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }


        /*
     点击获取验证码的按钮绑定事件
       */
        function bindClickBtnSms() {
            $('#btnSms').click(function () {
                $('.error-msg').empty();
                //获取用户输入的手机号
                //找到输入框的ID,根据ID获取值,如何找到手机号的那个ID?
                {#alert($('#id_mobile_phone').val());#}
                var mobilePhone = $('#id_mobile_phone').val();

                //向后台发送ajax请求,把手机号发送过去
                $.ajax({
                    url: "{% url 'send_sms' %}",//等价于/send/sms/
                    type: "GET",
                    data: {mobile_phone: mobilePhone, tpl: "login"},
                    dataType: "JSON",//将服务端返回的数据反序列化为字典,因为前端拿到的是字符串,而其实后端返回的是字典
                    success: function (res) {
                        {#res_dict = JSON.parse(res) 再用HttResponse返回时要写#}
                        //ajax请求发送成功之后,自动执行(回调)的函数,res就是后端返回的值
                        if (res.status) {
                            sendSmsRemind();
                        } else {
                            //错误信息
                            console.log(res);//返回的res是字典:{status:False, error:{mobile_phone:["错误信息",],code:["错误信息”,]}}
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }

        /*
     倒计时
      */
        function sendSmsRemind() {
            var $smsBtn = $('btnSms');

            $smsBtn.prop("disabled", true);  //禁用
            var time = 60;


            var remind = setInterval(function () {
                    $smsBtn.val(time + '秒重新发送');
                    time = time - 1;
                    if (time < 1) {
                        clearInterval(remind);
                        $smsBtn.val('点击获取验证码').prop('disabled', false);
                    }
                }, 1000
            )

        }


    </script>
{% endblock %}

在 form\account.py中:

class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    print("mobile_phone", mobile_phone)

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 验证手机号
    def clean_mobile_phone(self):
        """手机号校验的钩子函数"""

        # 校验数据前,都需要获取到被校验的数据
        mobile_phone = self.cleaned_data['mobile_phone']  # 获取用户提交的手机号

        # 判断短信模版是否有问题
        tpl = self.request.GET.get('tpl')
        mobile_phone = self.request.GET.get('mobile_phone')
        print(tpl)
        template_id = settings.TENCENT_SMS_TEMPLATE[tpl]
        if not template_id:
            # self.add_error('mobile_phone', '短信模版错误')
            raise ValidationError('短信模版错误')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                raise ValidationError('手机不存在')
        else:
            # 验证数据库中是否已有手机号
            if exists:
                raise ValidationError('手机号已存在')

        # 发短信
        code = random.randrange(1000, 9999)  # 生成随机验证码

        # 发送短信
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败,{}".format(sms['errmsg']))

        # 发送成功后,短信验证码写入redis(利用django-redis组件)
        conn = get_redis_connection()  # redis的连接获取到
        conn.set(mobile_phone, code, ex=60)  # 设置key,value,超时时间为60

        return mobile_phone


class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        # 查询数据库里是都有手机号,并且返回用户对象,即当前用户的一整个列表
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_object:
            raise ValidationError('手机号不存在')
        return user_object  # 返回用户对象

    def clean_code(self):
        code = self.cleaned_data['code']
        user_object = self.cleaned_data.get('mobile_phone')

        # 手机号不存在,则验证码无需再校验
        if not user_object:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(user_object.mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误,请重新输入')

        return code

在view\account.py中

def login_sms(request):
    '''短信登录'''
    if request.method == 'GET':
        form = LoginSMSForm()
        return render(request, 'web/login_sms.html', {'form': form})

    form = LoginSMSForm(request.POST)  # POST方式接收数据,后进行表单验证
    if form.is_valid():
        # 用户输入正确,登录成功
        user_object = form.cleaned_data['mobile_phone']
        # 把用户名写入到session中

        request.session['user_id'] = user_object.id
        request.session['user_name'] = user_object.username

        print(user_object.username)
        return JsonResponse({"status": True, 'data': "/index/"})
    return JsonResponse({"status": False, 'error': form.errors})

另外一种写法:不够简便,但是很好理解:

 在 form\account.py中:

class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()  # 查询数据库里是都有手机号
        # user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not exists:
            raise ValidationError('手机号不存在')
        return mobile_phone  # 返回用户对象

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')

        # 手机号不存在,则验证码无需再校验
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误,请重新输入')

        return code

在view\account.py中

def login_sms(request):
    '''短信登录'''
    if request.method == 'GET':
        form = LoginSMSForm()
        return render(request, 'web/login_sms.html', {'form': form})

    form = LoginSMSForm(request.POST)  # POST方式接收数据,后进行表单验证
    if form.is_valid():
        # 用户输入正确,登录成功
        mobile_phone = form.cleaned_data['mobile_phone']
        # 把用户名写入到session中
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()

        request.session['user_id'] = user_object.id
        request.session['user_name'] = user_object.username

        print(user_object.username, user_object.email)
        return JsonResponse({"status": True, 'data': "/index/"})
    return JsonResponse({"status": False, 'error': form.errors})

  3、用户名/密码登录:

 

 

 

 

 

3.1 python自动生成图片+写文字(即随机验证码)

3.2 Session&Cookie

3.3页面显示

3.4登录

 

3.1 python自动生成图片+写文字(即随机验证码)

https://www.cnblogs.com/wupeiqi/articles/5812291.html

pip install pillow

from PIL import Image

img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))

# 在图片查看器中打开
# img.show()

# 保存在本地
with open('code.png', 'wb') as f:
    img.save(f, format='png')  # 创建白板写入code.png里去,后缀名是png

效果如下:

在image_code.py中:
import random

from PIL import Image, ImageDraw, ImageFont, ImageFilter


def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母
        :return:
        """
        return chr(random.randint(65, 90))  # 数字转化为字符ASCII

    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)


if __name__ == '__main__':
    img_object, code = check_code()  # 上面返回的是一个元组即图片对象和验证码
    print(code)

    # 把图片内容写入本地文件
    with open('code.png', 'wb') as f:
        img_object.save(f, format='png')  # 创建白板写入code.png里去,后缀名是png
    # 把图片内容写到内存stream
    from io import BytesIO

    stream = BytesIO()
    img_object.save(stream, 'png')

    stream.getvalue()  # 获取图片内容

效果:

 

 3.2 Session&Cookie

 

 注:默认django的服务端session存储数据的默认过期时间是两周,但是可以通过代码去修改过期时间。客户端(浏览器端)写cookie设定超时。

 数据库表中对应的session数据:

图片验证码实现过程:

 

 注:服务器给浏览器不是直接返回图片,而是返回一个Html模板。还有各种文字和img标签。浏览器再次向图片的src地址发送请求从而获得图片。

3.3页面展示

不用ajax请求,而通过forn表单进行提交。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 为什么用session存储而不用redis 存储,因为如果用redis存储的话需要自己手动生成随机字符串并写到用户浏览器上,再把随机串当成key,code 当成value写到redis,并操作

Cooie,而Session恰好可以生成唯一的标识作为key,比较简单。

而之前发短信用redis,是因为唯一标识是用户填入的手机号,不需要自动生成唯一标识。所以可以用redis.

 

 

 

 

 

 

 

 

 

 

 

 

 在session中获取验证码的写法之一,但是最好是用.get去获取,因为验证码在session 可能会过期,就不存在了,要用get去获取

 

 其中一种用户和密码的写法:

在view\account.py中

def login(request):
    "“”用户名和密码登录"""
    if request.method == "Get":
        form = LoginForm(request)
        return render(request, 'web/login.html', {'form': form})
    form = LoginForm(request, data=request.POST)
    if form.is_valid():
        username = form.cleaned_data['username']
        password = form.cleaned_data['password']
        user_object = models.UserInfo.objects.filter(username=username, password=password).first()
        # (手机=username and pwd=pwd) or (邮箱=username and pwd=pwd)
        #
        # user_object = models.UserInfo.objects.filter(Q(email=username) | Q(mobile_phone=username)).filter(
        #     password=password).first()
        if user_object:
            # 用户名密码正确
            return redirect('index')
        form.add_error('username', '用户名或密码错误')
    return render(request, 'web/login.html', {'form': form})  # 页面展示

在forms\account.py中

class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label="用户名")
    password = forms.CharField(label="密码", widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label="图片验证码")

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密&返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """钩子 校验图片验证码是否正确"""
        # 读取用户输入的验证码
        code = self.cleaned_data['code']

        # 去session获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期,请重新获取')

        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入错误')

        return code

 

 

 

全部注册与登录所用到的全部代码:

在basic.html中

{% load static %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'plugin/bootstrap/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'plugin/font-awesome/css/fontawesome.min.css' %}">
    <style>
        .navbar-default {
            border-radius: 0;
        }
    </style>
    {% block css %} {% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <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="{% url 'index' %}">Tracer平台</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="#">产品功能</a></li>
                <li><a href="#">企业方案</a></li>
                <li><a href="#">帮助文档</a></li>
                <li><a href="#">价格</a></li>

            </ul>

            <ul class="nav navbar-nav navbar-right">
                <li><a href="{% url 'login' %}">登 录</a></li>
                <li><a href="{% url 'register' %}">注 册</a></li>

                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">武沛齐 <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">管理中心</a></li>

                        <li role="separator" class="divider"></li>
                        <li><a href="#"> 退 出</a></li>
                    </ul>
                </li>
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
{% block content %} {% endblock %}

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

在index.html中:

{% extends  'layout/basic.html' %}
{% load static %}

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

{% block  css %}
    <style>
        img {
            width: 100%;
        }
    </style>
{% endblock %}

{% block  content %}
    <div>
        <img src="{% static 'img/index/index-1.jpg' %}">
        <img src="{% static 'img/index/index-2.png' %}">
        <img src="{% static 'img/index/index-3.png' %}">
        <img src="{% static 'img/index/index-4.png' %}">
    </div>
{% endblock %}

在login.html中:

{% extends 'layout/basic.html' %}
{% load static %}

{% block title %} 用户登录 {% endblock %}

{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">用户登录</div>
        <form method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {#                    上述代码在html模板中隐去,在ModelForm中进行样式form-control的编写#}
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg">{{ field.errors.0 }}</span>
                            </div>
                            <div class="col-xs-5">
                                <img src="{% url 'image_code' %}" id="imageCode" title="点击更换图片">
                            </div>
                        </div>

                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {{ field }}
                        <span class="error-msg">{{ field.errors.0 }}</span>
                    </div>
                {% endif %}
            {% endfor %}

            <div style="float: right;">
                <a href="{% url 'login_sms' %}"> 短信验证码登录?</a>
            </div>


            <div class=" row">
                <div class="col-xs-3">
                    <input type="submit" class="btn btn-primary" value="登 录"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}
{% block js %}
    <script>
        $(function () {
            $('#imageCode').click(function () {
                var oldSrc = $(this).attr('src');
                $(this).attr('src', oldSrc + "?");   // /index/?name=123 相等于重新再发一次请求
            })
        })
    </script>
{% endblock %}

在login_sms.html中:

{% extends 'layout/basic.html' %}
{% load static %}

{% block title %} 用户短信登录 {% endblock %}

{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">用户短信登录</div>
        <form id="smsForm" method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {#                    上述代码在html模板中隐去,在ModelForm中进行样式form-control的编写#}
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg"></span>
                            </div>
                            <div class="col-xs-5">
                                <input id="btnSms" type="button" class="btn btn-default" value="点击获取验证码">
                            </div>
                        </div>

                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {{ field }}
                        <span class="error-msg"></span>
                    </div>
                {% endif %}
            {% endfor %}

            <div style="float: right;">
                <a href="{% url 'login' %}">用户名密码登录?</a>
            </div>


            <div class=" row">
                <div class="col-xs-3">
                    <input id="btnSubmit" type="button" class="btn btn-primary" value="登 录"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block js %}
    <script>
        //页面框架加载完成之后自动执行函数
        $(function () {
            bindClickBtnSms();
            bindClickSubmit();
        });

        /*
     点击登录
       */
        function bindClickSubmit() {
            $('#btnSubmit').click(function () {
                $('.error-msg').empty();
                //收集表单中的数据 $('#regForm').serialize()//所有字段数据+csrf token
                //数据ajax发送到后台
                $.ajax({
                    url: "{% url 'login_sms' %}",
                    type: "POST",
                    data: $('#smsForm').serialize(),//所有字段数据+ csrf token
                    dataType: "JSON",
                    success: function (res) {
                        if (res.status) {
                            {#跳转页面#}
                            location.href = res.data;
                        } else {
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }


        /*
     点击获取验证码的按钮绑定事件
       */
        function bindClickBtnSms() {
            $('#btnSms').click(function () {
                $('.error-msg').empty();
                //获取用户输入的手机号
                //找到输入框的ID,根据ID获取值,如何找到手机号的那个ID?
                {#alert($('#id_mobile_phone').val());#}
                var mobilePhone = $('#id_mobile_phone').val();

                //向后台发送ajax请求,把手机号发送过去
                $.ajax({
                    url: "{% url 'send_sms' %}",//等价于/send/sms/
                    type: "GET",
                    data: {mobile_phone: mobilePhone, tpl: "login"},
                    dataType: "JSON",//将服务端返回的数据反序列化为字典,因为前端拿到的是字符串,而其实后端返回的是字典
                    success: function (res) {
                        {#res_dict = JSON.parse(res) 再用HttResponse返回时要写#}
                        //ajax请求发送成功之后,自动执行(回调)的函数,res就是后端返回的值
                        if (res.status) {
                            sendSmsRemind();
                        } else {
                            //错误信息
                            console.log(res);//返回的res是字典:{status:False, error:{mobile_phone:["错误信息",],code:["错误信息”,]}}
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }

        /*
     倒计时
      */
        function sendSmsRemind() {
            var $smsBtn = $('btnSms');

            $smsBtn.prop("disabled", true);  //禁用
            var time = 60;


            var remind = setInterval(function () {
                    $smsBtn.val(time + '秒重新发送');
                    time = time - 1;
                    if (time < 1) {
                        clearInterval(remind);
                        $smsBtn.val('点击获取验证码').prop('disabled', false);
                    }
                }, 1000
            )

        }


    </script>
{% endblock %}

在register.html中:

{% extends 'layout/basic.html' %}
{% load static %}
{% block title %} 用户注册 {% endblock %}

{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">用户注册</div>
        <form id="regForm" method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {#                    上述代码在html模板中隐去,在ModelForm中进行样式form-control的编写#}
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg"></span>
                            </div>
                            <div class="col-xs-5">
                                <input id="btnSms" type="button" class="btn btn-default" value="点击获取验证码">
                            </div>
                        </div>

                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {#        <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">#}
                        {{ field }}
                        <span class="error-msg"></span>
                    </div>
                {% endif %}
            {% endfor %}
            <div class="row">
                <div class="col-xs-3">
                    <input id="btnSubmit" type="button" class="btn btn-primary" value="注册"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block js %}
    <script>
        //页面框架加载完成之后自动执行函数
        $(function () {
            bindClickBtnSms();
            bindClickSubmit();
        });

        /*
        点击提交(注册)
        */
        function bindClickSubmit() {
            $('#btnSubmit').click(function () {
                 $('.error-msg').empty();
                //收集表单中的数据 $('#regForm').serialize()//所有字段数据+csrf token
                //数据ajax发送到后台
                $.ajax({
                    url: "{% url 'register' %}",
                    type: "POST",
                    data: $('#regForm').serialize(),//所有字段数据+ csrf token
                    dataType: "JSON",
                    success: function (res) {
                        if (res.status) {
                            {#跳转页面#}
                            location.href = res.data;
                        } else {
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }

        /*
        点击获取验证码的按钮绑定事件
         */
        function bindClickBtnSms() {
            $('#btnSms').click(function () {
                $('.error-msg').empty();
                //获取用户输入的手机号
                //找到输入框的ID,根据ID获取值,如何找到手机号的那个ID?
                {#alert($('#id_mobile_phone').val());#}
                var mobilePhone = $('#id_mobile_phone').val();

                //向后台发送ajax请求,把手机号发送过去
                $.ajax({
                    url: "{% url 'send_sms' %}",//等价于/send/sms/
                    type: "GET",
                    data: {mobile_phone: mobilePhone, tpl: "register"},
                    dataType: "JSON",//将服务端返回的数据反序列化为字典,因为前端拿到的是字符串,而其实后端返回的是字典
                    success: function (res) {
                        {#res_dict = JSON.parse(res) 再用HttResponse返回时要写#}
                        //ajax请求发送成功之后,自动执行(回调)的函数,res就是后端返回的值
                        if (res.status) {
                            sendSmsRemind();
                        } else {
                            //错误信息
                            console.log(res);//返回的res是字典:{status:False, error:{mobile_phone:["错误信息",],code:["错误信息”,]}}
                            $.each(res.error, function (key, value) {
                                $("#id_" + key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }

        /*
        倒计时
         */
        function sendSmsRemind() {
            var $smsBtn = $('btnSms');

            $smsBtn.prop("disabled", true);  //禁用
            var time = 60;


            var remind = setInterval(function () {
                    $smsBtn.val(time + '秒重新发送');
                    time = time - 1;
                    if (time < 1) {
                        clearInterval(remind);
                        $smsBtn.val('点击获取验证码').prop('disabled', false);
                    }
                }, 1000
            )

        }
    </script>
{% endblock %}

在url.py中:

from django.urls import path
from web.views import account
from web.views import home

urlpatterns = [
    path('register/', account.register, name='register'),  # 反向解析后得到:register
    path('login/sms/', account.login_sms, name='login_sms'),  # 短信登录的方法
    path('login/', account.login, name='login'),  # 用用户名和密码登录的方法
    path('image/code/', account.image_code, name='image_code'),
    path('send/sms/', account.send_sms, name='send_sms'),

    path('index/', home.index, name='index'),
]

在forms\account.py中:

import random
import requests
from django import forms
from django.conf import settings

from django_redis import get_redis_connection
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

from web.forms.bootstrap import BootStrapForm
from utils import encrypt
from utils.tencent.sms import send_sms_single


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    # 重写规则,进行表单展示.增加正则表达式的校验
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "密码长度不能小于8个字符",
            'max_length': "密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': "重复密码长度不能小于8个字符",
            'max_length': "重复密码长度不能大于64个字符"
        },
        widget=forms.PasswordInput())
    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    # 利用钩子函数(用户名),对字段进行校验
    def clean_username(self):
        username = self.cleaned_data['username']

        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')  # 抛出异常后不会在返回值,所以cleaned_data中不会有username
            # self.add_error('username', '用户名已存在')  # 把错误信息进行添加但仍然会返回username的值
        return username

    # 利用钩子函数(邮箱),对字段进行校验
    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    # 利用钩子函数(密码),对字段进行校验
    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密&返回
        return encrypt.md5(pwd)

    # 利用钩子函数(确认密码),对字段进行校验
    def clean_confirm_password(self):
        # pwd = self.cleaned_data['password']
        pwd = self.cleaned_data.get('password')  # password是否校验通过,都可以拿到这个字段的值
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])  # 密文与密文进行匹配
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')
        return confirm_pwd

    # 利用钩子函数(手机号),对字段进行校验
    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已注册')
        return mobile_phone

    # 利用钩子函数(验证码),对字段进行校验

    def clean_code(self):
        code = self.cleaned_data['code']
        # mobile_phone = self.cleaned_data['mobile_phone']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)  # 根据手机号获取redis 里的code
        if not redis_code:  # redis里没有code
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')  # 字节变成字符串

        if code.strip() != redis_str_code():
            raise ValidationError('验证码错误,请重新输入')

        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    print("mobile_phone", mobile_phone)

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    # 验证手机号
    def clean_mobile_phone(self):
        """手机号校验的钩子函数"""

        # 校验数据前,都需要获取到被校验的数据
        mobile_phone = self.cleaned_data['mobile_phone']  # 获取用户提交的手机号

        # 判断短信模版是否有问题
        tpl = self.request.GET.get('tpl')
        mobile_phone = self.request.GET.get('mobile_phone')
        print(tpl)
        template_id = settings.TENCENT_SMS_TEMPLATE[tpl]
        if not template_id:
            # self.add_error('mobile_phone', '短信模版错误')
            raise ValidationError('短信模版错误')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                raise ValidationError('手机不存在')
        else:
            # 验证数据库中是否已有手机号
            if exists:
                raise ValidationError('手机号已存在')

        # 发短信
        code = random.randrange(1000, 9999)  # 生成随机验证码

        # 发送短信
        sms = send_sms_single(mobile_phone, template_id, [code, ])
        if sms['result'] != 0:
            raise ValidationError("短信发送失败,{}".format(sms['errmsg']))

        # 发送成功后,短信验证码写入redis(利用django-redis组件)
        conn = get_redis_connection()  # redis的连接获取到
        conn.set(mobile_phone, code, ex=60)  # 设置key,value,超时时间为60

        return mobile_phone


class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()  # 查询数据库里是都有手机号
        # user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not exists:
            raise ValidationError('手机号不存在')
        return mobile_phone  # 返回用户对象

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')

        # 手机号不存在,则验证码无需再校验
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')
        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误,请重新输入')

        return code


class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label="邮箱或手机号")
    password = forms.CharField(label="密码", widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label="图片验证码")

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密&返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """钩子 校验图片验证码是否正确"""
        # 读取用户输入的验证码
        code = self.cleaned_data['code']

        # 去session获取自己的验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期,请重新获取')

        if code.strip().upper() != session_code.strip().upper():
            raise ValidationError('验证码输入错误')

        return code

在forms/bootstrap.py中

 

# 面向对象的继承,在form和ModelForm中进行样式的设置
class BootStrapForm(object):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():  # 循环字段进行样式的设置
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

在views\account.py中

# 用户账户相关功能:注册、短信、登录、注销
from io import BytesIO
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse, redirect
from django.db.models import Q

from web.forms.account import RegisterModelForm, SendSmsForm, LoginSMSForm, LoginForm
from app01 import models
from utils.image_code import check_code


def register(request):
    # 注册
    if request.method == 'GET':
        form = RegisterModelForm()
        return render(request, 'web/register.html', {'form': form})
    # print(request.POST)  # 后台拿到的数据,传到ModelForm进行校验

    form = RegisterModelForm(data=request.POST)
    if form.is_valid():
        # 验证通过后,写入数据库(密码要是密文)
        # form.instance.password ="iudasndfiajsd;fj" #在保存之前将instance中的password进行重置
        form.save()  # 自动剔除数据库中没有的字段
        return JsonResponse({'status': True, 'data': '/login/'})  # 注册成功后进行前端跳转页面
    # 写入数据库的另外一种写法
    # data =form.cleaned_data
    # data.pop('code')
    # data.pop('confirm_password')
    # instance = models.UserInfo.objects.create(**data)

    # print(form.cleaned_data)  # 校验成功,输出cleaned_data
    return JsonResponse({'status': False, 'error': form.errors})


def send_sms(request):
    # 发送短信
    # print(request.GET)
    # mobile_phone = request.GET.get('mobile phone')
    # tpl = request.GET.get('tpl')  # register/login  根据tpl取短信模版
    # sms_template_id = settings.TENCENT_SMS_TEMPLATE9(tpl)
    form = SendSmsForm(request, data=request.GET)
    # 只是校验手机号:不能为空,格式是否正确
    if form.is_valid():  # 判断在form中是否校验成功
        # 验证通过后,发短信
        # 写redis
        return JsonResponse({'status': True})  # 表示短信发送成功

    return JsonResponse({'status': False, 'error': form.errors})  # 表示校验失败,所有错误信息都会放在form.errors中
    # return HttpResponse('{"k1":123}')


def login_sms(request):
    '''短信登录'''
    if request.method == 'GET':
        form = LoginSMSForm()
        return render(request, 'web/login_sms.html', {'form': form})

    form = LoginSMSForm(request.POST)  # POST方式接收数据,后进行表单验证
    if form.is_valid():
        # 用户输入正确,登录成功
        mobile_phone = form.cleaned_data['mobile_phone']
        # 把用户名写入到session中
        user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        request.session['user_id'] = user_object.id
        # request.session['user_name'] = user_object.user_name
        request.session.set_expiry(60 * 60 * 24 * 14)

        print(user_object.username, user_object.email)
        return JsonResponse({"status": True, 'data': "/index/"})
    return JsonResponse({"status": False, 'error': form.errors})


def login(request):
    "“”用户名和密码登录"""
    if request.method == "Get":
        form = LoginForm(request)
        return render(request, 'web/login.html', {'form': form})
    form = LoginForm(request, data=request.POST)
    if form.is_valid():
        username = form.cleaned_data['username']
        password = form.cleaned_data['password']
        # user_object = models.UserInfo.objects.filter(username=username, password=password).first()
        # (手机=username and pwd=pwd) or (邮箱=username and pwd=pwd)

        user_object = models.UserInfo.objects.filter(Q(email=username) | Q(mobile_phone=username)).filter(
            password=password).first()  # 利用Q构造复杂的查询条件
        if user_object:
            # 登录成功为止1
            request.session['user_id'] = user_object.id #用户信息保存在session中
            request.session.set_expiry(60 * 60 * 24 * 14)   #用户超时时间重新设定为两周

            return redirect('index')
        form.add_error('username', '用户名或密码错误')
    return render(request, 'web/login.html', {'form': form})  # 页面展示


def image_code(request):
    """生成图片验证码"""

    image_object, code = check_code()

    # 把图片上的文本内容即code放入session中
    request.session['image_code'] = code
    request.session.set_expiry(60)  # 主动修改session的过期时间为60s

    # 图片写入内存
    stream = BytesIO()
    image_object.save(stream, 'png')
    return HttpResponse(stream.getvalue())

    # 图片写入本地
    # with open('code.png', 'wb') as f:
    #     image_object.save(f, format='png')
    #
    # with open('code.png', 'rb') as f:
    #     data = f.read()
    #
    # return HttpResponse(data)

在home.py中

from django.shortcuts import render


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

在models.py中

from django.db import models


class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32,db_index=True) #db_index=True 创建索引
    email = models.EmailField(verbose_name='邮箱', max_length=32)
    mobile_phone = models.CharField(verbose_name='手机号', max_length=32)
    password = models.CharField(verbose_name='密码', max_length=32)

    def __str__(self):
        return self.username

    class Meta:
        db_table = "UserInfo"
        verbose_name_plural = '用户表'

在scripts/画图.py中:

from PIL import Image, ImageDraw

# 创建白板
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
# 写入文本
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
draw.text([0, 0], 'python', "red")
# 在图片查看器中打开
# img.show()

# 保存在本地
with open('code.png', 'wb') as f:
    img.save(f, format='png')  # 创建白板写入code.png里去,后缀名是png

在utils/tencent/sms.py中

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import ssl

ssl._create_default_https_context = ssl._create_unverified_context
from qcloudsms_py import SmsMultiSender, SmsSingleSender
from qcloudsms_py.httpclient import HTTPError

from django.conf import settings


def send_sms_single(phone_num, template_id, template_param_list):
    """
    单条发送短信
    :param phone_num: 手机号
    :param template_id: 腾讯云短信模板ID
    :param template_param_list: 短信模板所需参数列表,例如:【验证码:{1},描述:{2}】,则传递参数 [888,666]按顺序去格式化模板
    :return:
    """
    appid = settings.TENCENT_SMS_APP_ID
    appkey = settings.TENCENT_SMS_APP_KEY  # 自己应用Key
    sms_sign = settings.TENCENT_SMS_SIGN  # 自己腾讯云创建签名时填写的签名内容(使用公众号的话这个值一般是公众号全称或简称)
    sender = SmsSingleSender(appid, appkey)
    try:
        response = sender.send_with_param(86, phone_num, template_id, template_param_list, sign=sms_sign)
    except HTTPError as e:
        response = {'result': 1000, 'errmsg': "网络异常发送失败"}
    return response


def send_sms_multi(phone_num_list, template_id, param_list):
    """
    批量发送短信
    :param phone_num_list:手机号列表
    :param template_id:腾讯云短信模板ID
    :param param_list:短信模板所需参数列表,例如:【验证码:{1},描述:{2}】,则传递参数 [888,666]按顺序去格式化模板
    :return:
    """
    appid = settings.TENCENT_SMS_APP_ID
    appkey = settings.TENCENT_SMS_APP_KEY  # 自己应用Key
    sms_sign = settings.TENCENT_SMS_SIGN
    sender = SmsMultiSender(appid, appkey)
    try:
        response = sender.send_with_param(86, phone_num_list, template_id, param_list, sign=sms_sign)
    except HTTPError as e:
        response = {'result': 1000, 'errmsg': "网络异常发送失败"}
    return response

在utils/encrypt.py中:

# MD5加密功能

import hashlib

from django.conf import settings


def md5(string):
    """"MD5加密"""

    hash_object = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
    hash_object.update(string.encode('utf-8'))
    return hash_object.hexdigest()

utils/image_code.py中:

import random

from PIL import Image, ImageDraw, ImageFont, ImageFilter


def check_code(width=120, height=30, char_length=5, font_file='utils/Monaco.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母
        :return:
        """
        return chr(random.randint(65, 90))  # 数字转化为字符ASCII

    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)


if __name__ == '__main__':
    img_object, code = check_code()  # 上面返回的是一个元组即图片对象和验证码
    print(code)

    # 把图片内容写入本地文件
    with open('../scripts/code.png', 'wb') as f:
        img_object.save(f, format='png')  # 创建白板写入code.png里去,后缀名是png
    # 把图片内容写到内存stream
    from io import BytesIO

    stream = BytesIO()
    img_object.save(stream, 'png')

    stream.getvalue()  # 获取图片内容

总的路由分发:

bugmanagment/urls.py中:

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

# 主路由分发
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls'), name='app01'),  # 路由分发
    path('web/', include('web.urls')),  # 不以app01为前缀的,都走这个web的url,没有加namespace默认为前缀为空.

]

setting配置:(激活应用等):

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

MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

# ########  sms(模版) ########
# 腾讯云短信应用的 app_id
TENCENT_SMS_APP_ID = 6666666666
# 腾讯云短信应用的 app_key
TENCENT_SMS_APP_KEY = "66666666666666666666666"
# 腾讯云短信签名内容
TENCENT_SMS_SIGN = "python之路"

TENCENT_SMS_TEMPLATE = {
    'register': 548760,
    'login': 548762
}

try:
    from .local_settings import *
except ImportError:
    pass

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

以上为登录注册的所有代码,接下来来的内容为首页与登录注册之间的连接。

posted @ 2022-08-24 15:43  费皿啊  阅读(123)  评论(0)    收藏  举报