轻量级bug管理平台——注册

虚拟环境配置(略)‘

3、本地配置:

local_settings.py

4、代码仓库(码云)

 

 day02

1、内容回顾:

local_settings的作用?

本地配置信息

  • -开发
  • -测试     GIT仓库 
  • -运维

三个岗位都有各自的配置信息。

2.gitignore

git软件(对本地的代码做版本管理)。

  • git init
  • git add
  • git commit

码云/github /gitlab(自己的服务器托管平台)【代码托管,把本地代码上传上去】

综上:git在对项目做版本控制的时候需要忽略掉的文件。上传到码云自然不会上传了这些文件。

3、虚拟环境的作用?

让每个项目都有自己的环境,让项目之间进行隔离。开发:本地环境;线上:多环境隔离

 

把当前环境的所有模块,放在requirements.txt中:

pip freeze>requirements.txt

根据文件中所有的模块自动将所有的模块一一进行安装。

pip install -r requirements.txt

虚拟环境与上面两行代码结合:帮助其他用户或者角色快速搭建开发环境

requirements.txt放入仓库中管理起来终端命令如下:
git status
git add.
git commit -m 'requirements'
git push origin master


 
码云地址:gitee.com
1、腾讯发送短信功能:
  • 注册
  • 登录

使用文档:pythonav.com/wiki/detail/10/81/

 第一步:

 第二步:

安装SDK

pip install qcloudsms_py

第二步:基于SDK发送短信

全部代码如下:

  在settings.py中

STATIC_URL = '/static/'

# ########  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'

在local_settings.py中

#!/user/bin/env python
# -*- coding:utf-8 -*-

# 腾讯短信(真实的)
TENCENT_SMS_APP_ID = 1400302209
TENCENT_SMS_APP_KEY = "8cc5bf076454953306eb668187930004"

# 短信签名
TENCENT_SMS_SIGN = "python之路"

在view.py中:

import random
from django.http import HttpResponse
from django.shortcuts import render

from utils.tencent.sms import send_sms_single
from django.conf import settings

from django import forms
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError


# Create your views here.
def send_sms(request):
    """发送短信
       ?tpl=login -> 548762
       ?tpl=register ->548760
    """
    tpl = request.GET.get('tpl')
    template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
    if not template_id:
        return HttpResponse('模版不存在')
    code = random.randrange(1000, 9999)  # 随机验证码
    res = send_sms_single('15000258897', template_id, [code, ])
    # print(res)
    if res['result'] == 0:
        return HttpResponse('成功')
    else:
        return HttpResponse(res['errmsg'])  # 错误信息一般为(手机号格式错误)

 2.Django 的ModelForm

  •  自动生成标签
  • 表单验证

基本的注册功能:

在前端进行一个表单的循环:

 

 

 执行init方法对所有的字段进行处理,在ModelForm中快速生成样式和placeholder以支持页面的效果。

 在view.py中全部代码如下:

import random
from django.http import HttpResponse
from django.shortcuts import render

from utils.tencent.sms import send_sms_single
from django.conf import settings

from django import forms
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError


# Create your views here.
def send_sms(request):
    """发送短信
       ?tpl=login -> 548762
       ?tpl=register ->548760
    """
    tpl = request.GET.get('tpl')
    template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
    if not template_id:
        return HttpResponse('模版不存在')
    code = random.randrange(1000, 9999)  # 随机验证码
    print(code)
    # code 数据库
    # CODE ==
    # res = send_sms_single('15000258897', template_id, [code, ]) # 发送
    # # print(res)
    # if res['result'] == 0:
    #     return HttpResponse('成功')
    # else:
    #     return HttpResponse(res['errmsg'])  # 错误信息一般为(手机号格式错误)
    return HttpResponse('成功')


class RegisterModelForm(forms.ModelForm):
    # 重写规则,进行表单验证.增加正则表达式的校验
    mobile_phone = forms.CharField(
        label='手机号', validators=[RegexValidator(r'^[1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='重复密码',
        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 __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,)


def register(request):
    form = RegisterModelForm()  # 进行实例化
    return render(request, 'register.html', {'form': form})

在register.html中原代码如下:

 原来只需要在html中进行循环,加入验证码还要进行判断才行

 全部代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
    <style>
        .account {
            width: 400px;
            margin: 0 auto;
        }
    </style>
</head>
<body>
<div class="account">
    <h1 style="text-align: center;">注册</h1>
    <form>
        {% 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="clearfix">
                        <div class="col-md-6" style="padding-left:0; ">{{ field }}</div>
                        <div class="col-md-6"><input 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 }}
                </div>
            {% endif %}
        {% endfor %}
        <button type="submit" class="btn btn-primary">注册</button>
    </form>

</div>
</body>
</html>

 效果如下:

3.下一步思路

点击获取验证

  • 获取手机号
  • 向后台发送ajax(传递以下两个参数)
  1.     手机
  2.      tpl=register
  • 检验是否合法后,向手机发送验证码(ajax/sms/redis)
  • 验证码失效处理60s

 

 

 红色箭头是在点击获取验证码按钮时触发的,黄色按钮是在点击注册按钮时触发的。

红色箭头包含的技术点包含ajax请求,ajax发送到后台,后台接收到后通过短信API发送短信,再通过redis存储数据再加上超时时间存储.

4、redis基本操作

数据库和redis的区别:

  • 数据库将数据存储到文件
  • redis将数据存储到内存,并且支持超时时间

 

 

 

 mySQL(数据库)的默认端口为3306

redis的默认端口为6379

在连接之前要先安装redis模块。

4.1安装redis软件

windows版本的redis安装(本地开发)

redis安装在linux系统中(公司常用)

安装地址:https://github.com/microsoftarchive/redis/releases

详细说明地址:http://pythonav.com/wiki/detail/10/82/
打开windows的redis服务即可启动redis。

4.1 安装python 操作redis的模块

redis直接连接 

第一步:安装python操作redis模块
pip install redis

第二步:写代码去操作redis,如下图,可以写函数,也可以写在视图里。

就可以实现功能了。

   

作业:

  • ModelForm页面
  • register页面写ajax,手机号和模版字符串tpl  。 ( csrf-token的认证问题需要去解决)
  • 后台对两个参数进行校验
  • sms(发送短信)+redis
  • 进阶:
  •          倒计时效果
  •          注册按钮:字段校验+手机验证码的校验
  •          py操作redis:django-redis模块(而不是进行直接的端口连接)

 

day03用户认证

今日概要 所要完成的功能如下:

  • 注册
  • 短信验证码登录
  • 用户名密码登录

 

内容回顾&补充:

  • 虚拟环境 virtualenv(为每个项目创建独立虚拟环境)
  • requirements.txt(pip freeze>requirements.txt)  (环境所有的模块都放在requirements.txt中)
  • local_settings.py(本地配置)
  • gitignore(giti在项目版本管理时忽略掉的文件)
  • 腾讯云短信/阿里云短信(阅读文档、文档不清晰:谷歌、必应

     

     

    、搜狗)
  • 第三方工具的两个关键词汇如下:

                      API,提供URL你去访问这些URL并根据提示传参数【所有第三方工具都有】

基于API手动发送请求,发完请求后得到相应的请求数据。

requests.get("http://www.xxx.com//adsf/asdf/",json={...})

                    SDK,模块:下载安装模块,基于模块完成功能。(不是所有的第三方工具都有,已经包含了请求,只要下载模块即可)

在sms.py文件中已经写了一个包含请求的函数:

def func():
         return requests.get("http://www.xxx.com//adsf/asdf/",json={...})
pip install sms (导入模块)
sms.func()    用功能,不用再写请求

redis:帮助我们在内存可以存取数据的软件(基于内存的数据库)

第一步:在A主机安装redis&配置&启动

第二步:链接redis

       方法一:利用redis提供的客户端。

在终端链接数据库的命令为:

 

 

终端链接redis的命令为:

      方法二:利用相关模块

  •    安装模块
  •  pip install redis
  •    使用模块【不推荐直接连接】
  •  

  •    使用模块【推荐连接池】
  •  

     

  • django-redis,在django中“方便的”使用redis。
  • 不方便:redis模块+连接池的方法
  • 方便 :django-redis(内部用redis连接并且也提供了连接词)

安装:django-redis

pip install django-redis

使用:

#配置文件 settings.py (建议local_settings.py)

# redis

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",  # 安装redis的主机的IP和端口
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 1000, "decode_responses": True}
            # "PASSWORD": "密码",    #redis 密码
        }
    }
}

在视图函数中py文件中进行操作redis:

 

 get_redis_connection(),括号内不写参数默认是default,默认在django启动时,连接池已经创建,要用的时候进行连接即可。

 今日详细:

1、实现注册

  1.1展示注册页面

  •    1.1.1创建web的应用&注册app
  •    1.1.2模版文件路径处理
  •    1.1.3母板准备
  •    1.1.4 URL准备
  •    1.1.5注册页面显示

  1.2点击获取验证码

  •   1.2.1按钮绑定点击事件
  •   1.2.2获取手机号
  •   1.2.3发送ajax
  •   1.2.4手机号校验
  1. 不能为空
  2. 格式正确
  3. 没有注册过
  •   1.2.5验证通过 
  1.  发送短信
  2. 将短信保存到redis中(60s)

  1.3点击注册

 

  •    1.1.1创建web的应用&注册app
  •  

     

     

  • 1.1.2模版文件路径处理

注意:django默认找模版是找根目录之下的模版,即最外边的模版,没有的话按照应用注册的顺序来找,故项目中存在多个app应用同时存在的情况下,并且让每一个app维护自己的模版时可以如下操作,避免应用模版时找到别的应用的同名模版。

注:静态文件static也如此,与templates具有一样的查找规则。

 

 

 1.1.3母版准备

设置static加入css样式,img图片,js以及插件(下载bootstrap v3版本,和图标font-awesome) ,注意选择bootstrap的压缩版min.css进行选择 ,会更小,处理速度会更快

 

 母版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' %}">
  
</head>
<body>
<h1>头部菜单</h1>
{% 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>

在注册页面register.html的全部模块代码如下:

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

{% block title %}用户注册 {% endblock %}
{% block css %}

{% endblock %}

{% block content %}

{% endblock %}

{% block js %}

{% endblock %}

 1.1.4 URL准备

 三个url.py文件

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

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

]
from django.urls import path
from app01 import views

urlpatterns = [

    path(r'send/sms/', views.send_sms),
    path(r'register/', views.register, name='register'),  # 因为url的namespace为app01,反向解析后得到一个前缀,app01:register
]
from django.urls import path
from web.views import account

urlpatterns = [

    path(r'^register/$', account.register, name='register'),#反向解析后得到:register
]

  1.1.5注册页面显示

  •  导航条处理
  • 在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>
    </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="#">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="#">Link</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">Dropdown <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</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>

    效果展示:1.1.5注册页面展示

  • 母版中导航
  • 注册页面样式
  • ModelForm放到指定目录forms

全部有关代码为:

在web\register.html页面中

{% extends 'layout/basic.html' %}
{% load static %}
{% block title %} 用户注册 {% endblock %}
{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
{% endblock %}

{% block content %}
    <div class="account">
        <div class="title">用户注册</div>
        <form id="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="clearfix">
                            <div class="col-md-6" style="padding-left:0; ">{{ field }}</div>
                            <div class="col-md-6"><input 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 }}
                    </div>
                {% endif %}
            {% endfor %}
            <div class="row">
                <div class="col-xs-3">
                    <input type="button" class="btn btn-primary" value="注册"/>
                </div>
            </div>
        </form>
    </div>

{% endblock %}

{% block js %}

{% endblock %}

在account.css中:

.account {
    width: 400px;
    margin-top: 30px;
    margin-left: auto;
    margin-right: auto;
    border: 1px solid #f0f0f0;
    padding: 10px 30px 30px 30px;
    -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05);
    box-shadow: 5px 10px 10px rgba(0, 0, 0, .05);
}

.account .title {
    font-size: 25px;
    font-weight: bold;
    text-align: center;
}

.account .form-group {
    margin-bottom: 20px;
}

在forms\account.py中

from django import forms
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError


class RegisterModelForm(forms.ModelForm):
    # 重写规则,进行表单验证.增加正则表达式的校验
    mobile_phone = forms.CharField(
        label='手机号', validators=[RegexValidator(r'^[1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='重复密码',
        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 __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,)

相关文件为:

 

 1.2点击获取验证码

1.3点击注册

1.2.1按钮绑定点击事件

1.2.2获取手机号

 

 

1.2.3发送ajax

 

 

 

 另一种写法:

 

 全部代码如下:在regitster,html中

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

        /*
        点击获取验证码的按钮绑定事件
         */
        function bindClickBtnSms() {
            $('#btnSms').click(function () {
                //获取用户输入的手机号
                //找到输入框的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"},
                    success: function (res) {
                        //ajax请求发送成功之后,自动执行(回调)的函数,res就是后端返回的值
                        console.log(res);
                    }
                })
            })
        }

    </script>
{% endblock %}

在web\url中:

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


urlpatterns = [
    path('register/', account.register, name='register'),#反向解析后得到:register
    path('send/sms/', account.send_sms, name='send_sms'),
]

在views\account.py中多加一个函数:

def send_sms(request):
    # 发送短信
    # mobile_phone = request.GET.get('mobile phone')
    # tpl = request.GET.get('tpl')
    print(request.GET)
    return HttpResponse('成功')

 

1.2.4手机号校验

  • 不能为空
  • 格式正确
  • 没有注册过
  • 注意:django中的form可以生成页面上的标签也可以对数据进行校验
  • 在form或者modelform中,想用视图函数里面的参数或者值 ,可以构造初始化方法_init_方法,把想要的值传进来

所有校验代码如下:

在views\account.py视图函数中的代码如下:

# 用户账户相关功能:注册、短信、登录、注销
from django.shortcuts import render, HttpResponse
from web.forms.account import RegisterModelForm, SendSmsForm


def register(request):
    # 注册
    form = RegisterModelForm()
    return render(request, 'web/register.html', {'form': form})


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():
        pass

    return HttpResponse('成功')

在forms\account.py中用form进行校验:

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

    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')
        template_id = settings.TENCENT_SMS_TEMPLATE9(tpl)
        if not template_id:
            raise ValidationError('短信模版错误')

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

        return mobile_phone

 

1.2.5验证通过

  • 发送短信
  • 将短信保存到redis中(60s) ,以便于以后再去获取。

 

在views\account.py视图函数中的代码如下:

# 用户账户相关功能:注册、短信、登录、注销
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse
from web.forms.account import RegisterModelForm, SendSmsForm


def register(request):
    # 注册
    form = RegisterModelForm()
    return render(request, 'web/register.html', {'form': form})


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中

在forms\account.py中用form进行校验:

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 utils.tencent.sms import send_sms_single


class RegisterModelForm(forms.ModelForm):
    # 重写规则,进行表单验证.增加正则表达式的校验
    mobile_phone = forms.CharField(
        label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(
        label='密码',
        widget=forms.PasswordInput())

    confirm_password = forms.CharField(
        label='重复密码',
        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 __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,)


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

    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')
        template_id = settings.TENCENT_SMS_TEMPLATE9(tpl)
        if not template_id:
            raise ValidationError('短信模版错误')

        # 验证数据库中是否已有手机号
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        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

 得到的效果:

 

 

1.2.6成功失败

  • 失败,错误信息
  • register.html关代码如下:

 

        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) {
                            console.log('发送成功,倒计时')
                        } else {
                            //错误信息
                            console.log(res);//返回的res是字典:{status:False, error:{mobile_phone:["错误信息",],code:["错误信息”,]}}
                            $.each(res.error,function (key,value){
                                $("#id_"+key).next().text(value[0]);
                            })
                        }
                    }
                })
            })
        }

在view\account.py中:

 

 

# 用户账户相关功能:注册、短信、登录、注销
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse
from web.forms.account import RegisterModelForm, SendSmsForm


def register(request):
    # 注册
    form = RegisterModelForm()
    return render(request, 'web/register.html', {'form': form})


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}')

 钩子函数知识点答疑:

 

 

  • 成功, 倒计时
    • disabled属性

 

$("#btnSms").prop("disabled",true);#添加disabled属性,即不可操作。
$("#btnSms").prop("disabled",false);#移除disabled属性,即可操作。

 

 

    • 定时器
  • 每一秒执行一次函数:
var obj = setInterval(function(){
    console.log(123);
},1000)

clearInterval(obj);#关闭定时器
#完成定时器功能
var time = 60; var obj =setInterval(function(){ time = time - 1;
  if(time<1{
  clearInterval(obj);

}
},
1000)
  • 1.3点击注册

内容总结

  • 视图view.py->views目录
  • 模版,根目录templates->根据app注册顺序去每一个app的templates中
  • 静态文件,同上static
  • 项目中多个app且想要各自模板、静态文件隔离,建议通过app名称再进行嵌套即可。
  • 路由分发
    •   include
    •        namespace(app中name重名时,利用namespace进行区分)
  •  母板(四块)
title
css
content
js
  • bootstrap导航条、去除圆角、container样式(有边距)、图标font-awesome
  • ModelForm生成HTML标签、自动生成ID:格式为 id_字段名
  • 绑定事件,发送ajax请求

 

  $.ajax({
                    url: '/index/',#提交的地址
                    type: 'GET',#提交的方式
                    data: {},#提交的数据 
                    dataType: "JSON",#数据返回之后的格式
                    success: function (res) {
                    console.log(res) 
                    #后台接收请求处理完成后返回的结果
} 
})    
  • Form&ModelForm可以进行表单验证
    form = sendSmsForm(data = request.POST)#QueryDict
    form = sendSmsForm(data = request.GET)#QueryDict
  • Form&ModelForm中如果想要用视图中的值or(request),可以通过重写form的__init__方法
  • class SendSmsForm(forms.Form):
        mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
      
        def __init__(self, request, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.request = request  #接下来在其他的钩子中就可以用到这个request了  

     

  • 短信
  • redis(django-redis)
  • 倒计时(定时器和disabled属性)

内容回顾

  • 项目规则
    • 创建项目:静态、视图、路由(创建文件夹和文件)
  • ajax
  $.ajax({
                    url: '...',
                    type:"GET",
                    data: {},
                    dataType: "JSON",
                    success: function (res) {
                          
}
})
  • Form/ModelForm想使用视图中的数据,例如:request
重写ModelForm/Form的_init_方法,把想要的数据传递。
  • 注意:重写的功能还要执行一下副类的功能,原有的功能不能丢失。
  • django-redis

今日概要

  • 点击注册
  • 用户登录
    •   短信验证码登录
    •   手机or邮箱/密码登录
  • 项目管理(创建&星标)

今日详细

1、点击注册

1.1点击收集数据&ajax(发送)

 $.ajax({
                    url: "{% url 'register' %}",
                    type: "POST",
                    data: $('#regForm').serialize(),#所有字段数据+csrf token
                    dataType: "JSON",
                    success: function (res) {
                    console.log(res);
                  }
})                       

1.2数据校验(每个字段)

 

 

 

 

 

 

 

 

1.3校验通过后,用户注册成功后,写入数据库

1.4陈硕Bug

所有代码如下:

在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 utils import encrypt
from utils.tencent.sms import send_sms_single


class RegisterModelForm(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 __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,)

    # 利用钩子函数(用户名),对字段进行校验
    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 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

在views\account.py中:

# 用户账户相关功能:注册、短信、登录、注销
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse
from web.forms.account import RegisterModelForm, SendSmsForm
from app01 import models


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}')

在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()

在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 %}

 

 

 

 

 


posted @ 2022-07-13 18:26  费皿啊  阅读(159)  评论(0)    收藏  举报