轻量级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(传递以下两个参数)
- 手机
- 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.5验证通过
- 发送短信
- 将短信保存到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 %}


1.1.5注册页面展示




浙公网安备 33010602011771号