文章目录
这篇博客算是一个十分庞大的实践了,之前的每个功能实现都是很简单的——调用调用框架,自己写一些简单的视图函数。
今天我将完成对博客系统注册内容的追加,博客前端页面的完善两个部分。重点在于前端页面的完善了(要用很多js)
我的前端水平不高,jquery学过之后就忘记了,因此我用原生js代码来实现我想要的功能。
技术概要
- 构建django模板,追加User的内容
- 构建前端页面
- 使用js完成用户注册页面信息的验证
- 验证用户名是否合法
- 验证密码是否符合要求
- 引入发送短信模块,给注册手机发送短信验证码
- 引入邮件发送模块,发送注册成功提示
- 表单的处理,把表单内容写入模型
下面我将一一实现这些功能
构建django模板,追加User内容
模型构造
之前我用的用户模型,直接就是from django.contrib.auth.models import User这个用户模型,它的自定义字段有很多是不符合实际需求的,在这里,我对它进行一个扩展。
打开/account/models.py,编辑这个模板文件,代码如下所示
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
mobile = models.CharField(max_length=11, verbose_name="手机号码")
email = models.EmailField(default='', verbose_name="电子邮箱")
nickname = models.CharField(max_length=30, verbose_name="昵称")
username = models.OneToOneField(User, on_delete=models.CASCADE, default=None, verbose_name="用户名")
QQ = models.CharField(max_length=10, verbose_name="QQ号码")
内容还是比较易于理解的,在这里其实是创建了一个一对一的用户模型,把它和User紧密相连,其他的都是正常的字段创建,没有什么好介绍的。
后台管理
后台管理都是在admin.py中完成,这个应用也不例外,打开/account/admin.py,写如下代码即可:
from django.contrib import admin
from .models import UserProfile
# Register your models here.
admin.site.register(UserProfile)
我没有去实现高级后台管理功能,因为我的目的是先把网站上线,后续功能可以慢慢追加。其实也不一定有必要了,把所有的东西如果前台都能展示,其实不要这个后台管理界面也是没有问题的。
构建前端页面
表单构建
所有的表单都是在form.py中完成的,代码如下
from django import forms
from .models import UserProfile
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
class SignUpForm(forms.ModelForm):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = UserProfile
fields = ['username', 'password', 'mobile', 'email', 'nickname', 'QQ']
前端页面
前端页面这里,我没有用django自带的表单转页面功能,你问我为什么,因为我弄了很久都不知道怎么给它自动生成的表单加样式,丑的一批比较不雅观,因此自定义了表单。
表单这里要注意的就是表单的name属性,这个是最后传给后台由后台接收的
{% extends "base_cn.html" %}
{% block title %}
注册界面
{% endblock %}
{% block content %}
<div class="container">
<h1 class="text-center" style="margin-bottom: 15px;">欢 迎 加 入</h1>
<!-- 表单样式非常难看,用JS来重写样式(因为以我的水平没法给生成的表单加属性,只能用js -->
<form onsubmit="return checkForm();" class="form-horizontal" action="/account/signup/" method="POST">
{% csrf_token %}
<div class="form-group">
<label for="username" class="col-sm-2 control-label">用户名</label>
<div class=" col-sm-5">
<input type="text" name="username" id="username" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 请输入你的用户名,用户名长度应在5位以上</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">密码</label>
<div class="col-sm-5">
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 请输入密码,密码长度应在8位以上</div>
</div>
<div class="form-group">
<label for="password_repeat" class="col-sm-2 control-label">确认密码</label>
<div class="col-sm-5">
<input type="password" name="password_repeat" id="password_repeat" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 请确认密码,密码长度应在8位以上</div>
</div>
<div class="form-group">
<label for="mobile" class="col-sm-2 control-label">手机号码</label>
<div class="col-sm-5">
<input type="text" name="mobile" id="moblie" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 请输入你的手机号码</div>
</div>
<div class="form-group">
<label for="mobile_identy" class="col-sm-2 control-label">短信验证</label>
<div class="col-sm-3">
<input type="text" name="mobile_identy" id="mobile_identy" class="form-control">
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-info">发送短信验证</button>
</div>
<!-- <div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span>简介性的字,可用AJAX进行动态刷新</div> -->
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">电子邮件</label>
<div class="col-sm-5">
<input type="email" name="email" id="email" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 请输入你的电子邮箱</div>
</div>
<div class="form-group">
<label for="nickname" class="col-sm-2 control-label">昵称</label>
<div class="col-sm-5">
<input type="text" class="form-control" name="nickname" id="nickname">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 输入昵称,这是你展示给其他用户的名称</div>
</div>
<div class="form-group">
<label for="QQ" class="col-sm-2 control-label">QQ号码</label>
<div class="col-sm-5">
<input type="text" name="QQ" id="QQ" class="form-control">
</div>
<div class="col-sm-5" style="height: 34px;line-height: 34px;"><span
class="glyphicon glyphicon-remove"></span> 留下你的QQ吧,让我可以联系到你</div>
</div>
<div class="form-group">
<label for="nickname" class="col-sm-2 control-label"></label>
<div class="col-sm-5">
<button class="btn btn-primary btn-lg">提交注册</button>
</div>
</div>
</form>
</div>
<script src="/static/js/signUp.js"></script>
{% endblock %}
视图函数
下面那个函数就是表单的注册视图函数了,可以看到我注释掉了后台的表单生成,我自己写前端页面并构建联系,当请求是request时候,后台打印前端传来的数据,看下有没有问题即可。
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
from .forms import LoginForm, SignUpForm
# Create your views here.
def user_login(request):
if request.method == 'GET':
# 自动生成表单
login_form = LoginForm()
context = {}
context['form'] = login_form
return render(request, "account/login.html", context=context)
if request.method == 'POST':
#用POST请求来初始化表单
login_form = LoginForm(request.POST)
# 判断用户输入是否合法
if login_form.is_valid():
# 用键值对存储了表单中的数据
data = login_form.cleaned_data
user = authenticate(username=data['username'], password=data['password'])
if user:
login(request, user)
request.session['username'] = data['username']
return redirect('/')
else:
return HttpResponse('账号或密码错误')
else:
return HttpResponse('登录内容有误')
def user_signup(request):
if request.method == 'GET':
# signup_form = SignUpForm()
# context = {}
# context['form'] = signup_form
return render(request, 'account/signUp.html'\
# , context=context
)
else:
print(request.POST)
return HttpResponse("提交成功")
现在执行服务,在前台注册页面随便填点数据,提交一下,会看到后台打印如下内容:
<QueryDict: {‘csrfmiddlewaretoken’: [‘anZbYCNJ0XjxN1QJARrjLMEen7i6OPRtoX3JVBYnIxWXhfGbbIDPSE6rVGXAwzGT’], ‘username’: [‘wangxiong’], ‘password’: [‘931225’], ‘password_repeat’: [‘ewfwef’], ‘mobile’: [‘威风威风’], ‘mobile_identy’: [‘威锋网’], ‘email’: [‘13961252501@139.com’], ‘nickname’: [‘87527’], ‘QQ’: [‘74745’]}>
前端则会显示
提交成功
说明数据处理没问题,既然如此,就把所有的验证工作交给前端由AJAX完成吧!完成js部分后,我再进行后台的数据处理,把表单内容提交进数据库。
使用js完成用户注册页面信息的验证
因为我的前端js学的也不是很好,只是勉强应用阶段,所以我就不把那些函数封装的那么好了,直接一步一步来了,毕竟我的主要任务也不是玩前端的,GO!
在前端界面的最后我已经引入了script文件,现在开始编写/static/js/signUp.js文件,由于js我也不熟悉,所以我很有必要把注释写的清楚一些,也方便自己日后回顾。
先把用户名验证补上,后面的抽时间再增加,其实原理都是一样的,用js查元素的值,根据不同的情况再返回不同的值
要注意的就是这里面的正则表达式了,确实只要有一段时间不用,正则就忘得很快了,心累,都是现用现查现写的
var username = document.getElementById("username");
var password = document.getElementById("password");
var password_repeat = document.getElementById("password_repeat");
var mobile = document.getElementById("mobile");
var mobile_identy = document.getElementById("mobile_identy");
var email = document.getElementById("email");
var nickname = document.getElementById("nickname");
var QQ = document.getElementById("QQ");
var check1 = 0, //验证用户名
check2 = 0, //验证密码
check3 = 0, //确认密码
check4 = 0, //手机号码
check5 = 0, //短信验证码
check6 = 0, //电子邮箱
check7 = 0, //昵称
check8 = 0; //QQ号码
window.onload = function () {
//验证用户名函数
username.onblur = function () {
//js的正则表达式,很有必要
var patrn = /^[a-zA-Z0-9._/]{5,20}$/;
var usr = username.value;
check1 = patrn.test(usr);
if (check1 == true) {
document.getElementsByClassName("check")[0].innerHTML = "<span></span> 该用户名合法";
document.getElementsByClassName("check")[0].children[0].setAttribute("class", "glyphicon glyphicon-ok");
document.getElementsByClassName("check")[0].children[0].setAttribute("style", "color:green;");
}
if (check1 == false) {
document.getElementsByClassName("check")[0].innerHTML = "<span></span> 长度应在5到20位;可以是字母、数字、下划线";
document.getElementsByClassName("check")[0].children[0].setAttribute("class", "glyphicon glyphicon-remove");
document.getElementsByClassName("check")[0].children[0].setAttribute("style", "color:red;");
}
}
//验证密码函数
password.onblur = function () {
var patrn = /^[a-zA-Z0-9._/]{8,20}$/;
var pwd = password.value;
check2 = patrn.test(pwd);
if (check2 == true) {
document.getElementsByClassName("check")[1].innerHTML = "<span></span> 该密码符合要求";
document.getElementsByClassName("check")[1].children[0].setAttribute("class", "glyphicon glyphicon-ok");
document.getElementsByClassName("check")[1].children[0].setAttribute("style", "color:green;");
}
if (check2 == false) {
document.getElementsByClassName("check")[1].innerHTML = "<span></span> 请输入密码,密码长度应在8位以上";
document.getElementsByClassName("check")[1].children[0].setAttribute("class", "glyphicon glyphicon-remove");
document.getElementsByClassName("check")[1].children[0].setAttribute("style", "color:red;");
}
}
//验证密码确认函数
password_repeat.onblur = function () {
check3 = (password.value == password_repeat.value && check2);
if (check3 == true) {
document.getElementsByClassName("check")[2].innerHTML = "<span></span> 密码验证成功";
document.getElementsByClassName("check")[2].children[0].setAttribute("class", "glyphicon glyphicon-ok");
document.getElementsByClassName("check")[2].children[0].setAttribute("style", "color:green;");
}
if (check3 == false) {
document.getElementsByClassName("check")[2].innerHTML = "<span></span> 两次密码不一致,请重新来过";
document.getElementsByClassName("check")[2].children[0].setAttribute("class", "glyphicon glyphicon-remove");
document.getElementsByClassName("check")[2].children[0].setAttribute("style", "color:red;");
}
}
//验证手机号码函数,手机设置为11位的纯数字,在模型中手机号码是以字符串存储的,还算比较方便
mobile.onblur = function () {
var patrn = /^1[0-9]{10}$/;
var num = mobile.value;
check4 = patrn.test(num);
if (check4 == true) {
document.getElementsByClassName("check")[3].innerHTML = "<span></span> 手机号暂且是对的";
document.getElementsByClassName("check")[3].children[0].setAttribute("class", "glyphicon glyphicon-ok");
document.getElementsByClassName("check")[3].children[0].setAttribute("style", "color:green;");
}
if (check4 == false) {
document.getElementsByClassName("check")[3].innerHTML = "<span></span> 手机号最起码是11位吧?";
document.getElementsByClassName("check")[3].children[0].setAttribute("class", "glyphicon glyphicon-remove");
document.getElementsByClassName("check")[3].children[0].setAttribute("style", "color:red;");
}
}
email.onblur = function () {
var patrn = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
var Edress = email.value;
check6 = patrn.test(Edress);
if (check6 == true) {
document.getElementsByClassName("check")[5].innerHTML = "<span></span> 是合法邮箱";
document.getElementsByClassName("check")[5].children[0].setAttribute("class", "glyphicon glyphicon-ok");
document.getElementsByClassName("check")[5].children[0].setAttribute("style", "color:green;");
}
if (check6 == false) {
document.getElementsByClassName("check")[5].innerHTML = "<span></span> 请填写正确的邮箱";
document.getElementsByClassName("check")[5].children[0].setAttribute("class", "glyphicon glyphicon-remove");
document.getElementsByClassName("check")[5].children[0].setAttribute("style", "color:red;");
}
}
}
function checkForm() {
alert(check1);
return false;
}
后台发送短信验证码
我用的是腾讯云的发送短信服务(不是打广告哈),他的接口文档是这样的
'''
以下为初始设置
'''
# 短信应用 SDK AppID
appid = 1400009099 # SDK AppID 以1400开头
# 短信应用 SDK AppKey
appkey = "9ff91d87c2cd7cd0ea762f141975d1df37481d48700d70ac37470aefc60f9bad"
# 需要发送短信的手机号码
phone_numbers = ["21212313123", "12345678902", "12345678903"]
# 短信模板ID,需要在短信控制台中申请
template_id = 7839 # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
# 签名
sms_sign = "腾讯云" # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请
# 主要程序
from qcloudsms_py import SmsSingleSender
from qcloudsms_py.httpclient import HTTPError
ssender = SmsSingleSender(appid, appkey)
params = ["5678"] # 当模板没有参数时,`params = []`
try:
result = ssender.send_with_param(86, phone_numbers[0],
template_id, params, sign=sms_sign, extend="", ext="") # 签名参数未提供或者为空时,会使用默认签名发送短信
except HTTPError as e:
print(e)
except Exception as e:
print(e)
print(result)
作为一个比较低端的APIcaller,只要看懂这些代码,然后修改成自己适用的即可。接着和前端ajax衔接上就行了。
创建一个app名叫ajax,专用来处理ajax请求(之前有一个地方是用ajax请求的,就算了——先实现项目,后期再改)
python manage.py startapp ajax
输入这段代码,生成一个新的应用,这几步已经形成肌肉习惯
- settings.py增加应用名称
- 设置url
- 设置视图函数
视图函数是重点了,因为我要在视图函数里生成随机验证码发送给前端,在这里还得调用一下腾讯云的发短信服务。
params的构造很简单,构造一个六位的随机数出来,发送短信模块腾讯云有sdk文档,我就不打广告了,自己可以去看,最终把验证码以Httpresponse的形式传回给前端
from django.shortcuts import render
from django.http import HttpResponse
from qcloudsms_py.httpclient import HTTPError
from qcloudsms_py import SmsSingleSender
from .message import appid, appkey, template_id, sms_sign
import random
def message(request):
phone_numbers = [request.GET['num']]
print(phone_numbers)
params = [''.join([str(random.randrange(0, 10)) for i in range(6)])]
print(params)
ssender = SmsSingleSender(appid, appkey)
ssender.send_with_param(86, phone_numbers[0],
template_id, params, sign=sms_sign, extend="", ext="")
return HttpResponse(params[0])
发短信的js代码是这样的
mobile_button.onclick = function () {
var num = mobile.value;
if (check4 == true) {
alert("已发送验证码到" + num);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
idendy = xhr.responseText;
}
}
xhr.open("get", "/ajax/message?num=" + num, true);
xhr.send(null);
} else {
alert("手机号码不对是不能发送验证码的");
}
}
最后的最后来个form表单验证
function checkForm() {
if (check1 && check2 && check3 && check4 && check5 && check6 && check7 && check8) {
return true;
} else {
return false;
}
}
总结
验证依旧不完整,你问我少了哪块,我只能说少了用户名查重这块我还没验证,这块也是要发送ajax请求的。后续我就自己完善了,不必再发博客里了,毕竟这东西都是相通的