django练手( 十一):实现网站的用户注册功能
一、注册功能开始之前,我对网站的目录进行了优化,具体优化的内容如下:
- app下新建form文件夹,form文件夹下新建app文件夹,最后一个app文件夹下新建account.py文件。用来写跟账号相关的form。例如:注册,登录,修改密码等。
- app下新建views文件夹,views文件夹下新建app文件夹。在views文件上点击鼠标右键-重构-移动目录,把views.py文件移动到app-views-app目录下。用来存放跟账号相关的view。
- 在app文件夹下的templates文件下,新建app文件夹,在最后一个app文件夹下新建layout文件夹,把basic.html文件通过重构-移动目录的方式移动到app-templates-app-layout文件夹下。把index.html移动到app-templates-app文件夹下。在这个文件夹下新建register.html文件,作为注册页面。
- 在app文件夹下新建urls.py文件,作为app内的路由文件使用。
- 整体优化完成的网站目录如下图:
二、编写注册Form类
在app-form-app-account.py 文件中编写RegisterForm,用来进行注册表单的生成和注册数据的验证。得益于django得auth组件给注册提供了大量得验证功能,所以我的代码中只写了手机号格式的验证和两次输入密码一致性的验证。另外,不知为何,我没有找到django自动提供的密码框功能,只能重新写了一遍。具体的代码如下:
from django import forms
from django.core.exceptions import ValidationError
from app.models import MyUser
import re
# 创建用户注册数据的验证逻辑
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(),
label='密码', max_length=25,
min_length=8,
help_text='请输入密码', error_messages={'max_length': '密码长度不能大于25个字符',
'min_length': '密码长度不能小于8个字符'}
)
confirm_password = forms.CharField(min_length=8, max_length=25, label='重复密码', help_text='请输入重复密码',
widget=forms.PasswordInput())
class Meta:
model = MyUser
fields = ['username', 'password', 'confirm_password', 'email', 'phone_num', 'address']
def __init__(self, *args, **kwargs):
"""重写父类的__init__方法,给每个字段的widget增加attrs属性"""
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs['class'] = "form-control"
field.widget.attrs['placeholder'] = '请输入{}'.format(field.label)
# 校验密码的一致性
def clean_confirm_password(self):
confirm_password = self.cleaned_data['confirm_password']
password = self.cleaned_data.get('password', '')
if confirm_password != password:
raise ValidationError('两次输入的密码不一致')
# 校验手机号的格式
def clean_phone_num(self):
phone_num = self.cleaned_data['phone_num']
phone_num_re = re.match(r"^1[35678]\d{9}$", phone_num)
if not phone_num_re:
raise ValidationError('手机号格式不正确')
return phone_num
三、编写注册模板
在app-templates-app-register.html中编写注册模板。具体的代码如下:
{% extends 'app/layout/basic.html' %}
{% load static %}
{% block css %}
<link rel="stylesheet" href="{% static 'css/account.css' %}">
{% endblock %}
{% block content %}
<div class="account">
<div class="title"> 注册</div>
<form id="reg_form" method="post">
{% csrf_token %}
<div class="form-group">
{% for field in fields %}
<div class="form-group">
<label for={{ field.id_for_label }}>{{ field.label }}</label>
{{ field }}
<span class="error-msg"></span>
</div>
{% endfor %}
</div>
<input id="btnSubmit" type="button" class="btn btn-default" value="注册">
</form>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(function () {
btnSubmitClick(); //单击注册按钮,向注册视图发送Post请求。
})
/*
单击注册按钮,向注册视图发送POST请求的功能实现。
1、单击注册按钮后,清除所有错误信息;
2、获取表单数据。这里用到了JQUERY的.serialize()方法。serialize() 方法创建以标准 URL 编码表示的文本字符串。
它的操作对象是代表表单元素集合的 jQuery 对象。
3、用ajax的异步方法向后台发起POST请求。请求成功后,如果后台返回的数据的状态是True(注册成功),则跳转到index.html页面。
如果数据的状态是Falase(注册失败),则把错误信息显示在页面上。
* */
function btnSubmitClick() {
$('#btnSubmit').click(
function () {
$('.error-msg').empty();
//收集表单中的数据(找到每一个字段,发POST请求)
let data = $('#reg_form').serialize(); //所有数据+csrf token
//数据通过Ajax发送到后台
$.ajax({
url: "{% url 'app:register' %}",
type: 'POST',
data: data,
dataType: "JSON",
async: false,
success: function (res) {
//传输成功后执行的函数
if (res.status) {
location.href='/';
} else {
$.each(res.errors, function (key, value) {
$("#id_" + key).next().text(value);
})
}
},
errors: function () {
console.log("请求失败");
}
})
}
)
}
</script>
{% endblock %}
四、编写注册视图
在app-views-app-views.py文件下编写注册视图。值得注意的是,执行注册动作时,使用了auth组件的create_user()方法,没有使用form.save()方法。这是因为form.save()保存的密码是明文的,而create_user()方法保存的密码是加密的。具体代码如下:
def register(request):
"""
1、如果是GET请求,向注册模板返回表单数据;
2、如果是POST请求,执行注册动作;如果注册成功,向模板返回status的值是True。否则,返回status返回的值是False,并把错误信息返回给模板。
"""
if request.method == 'GET':
fields = RegisterForm()
return render(request, 'app/register.html', {'fields': fields})
if request.method == 'POST':
data = request.POST
fields = RegisterForm(data)
if fields.is_valid():
username = request.POST.get('username', '')
password = request.POST.get('password', '')
email = request.POST.get('email', '')
phone_num = request.POST.get('phone_num', '')
address = request.POST.get('address', '')
# 注册
user = MyUser.objects.create_user(username=username, password=password, email=email, phone_num=phone_num,
address=address)
return JsonResponse({"status": True})
else:
return JsonResponse({"status": False, "errors": fields.errors})
五、编写注册路由
我把网站的路由规划为两层:
1. 网站到app的路由。
2.app到视图的路由。
两层路由分别保存在网站根目录下的urls.py文件和app下的urls.py文件中。当前,这两个文件中的代码分别如下:
1. 网站到app的路由。
from django.contrib import admin
from django.urls import path, include
from app.views.app.views import *
urlpatterns = [
# 网站到app的路由,为便于以后扩展为多个app,此处使用了命名空间的写法
path('app/', include(('app.urls', 'app'), namespace='app')),
# 网站主页的路由
path('', index, name='index'),
# 管理后台的路由
path('admin/', admin.site.urls),
]
2.app到视图的路由
from django.urls import path
from app.views.app.views import register
urlpatterns = [
path('register/', register, name='register'), # 注册的路由
]
六、对注册页面的美化
注册的表单样式,我使用了bootstrap3上“全局css样式-表单”的基本样式。但是,只用这个样式的美观度还是有些不够,为此,我专门写了一个CSS文件,对表单的样式进行美化。
css文件路径是app-static-css-account.css。css文件的代码如下:
.account {
width: 400px;
margin: 30px 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 {
text-align: center;
font-size: 25px;
font-weight: bold;
}
.account .form-group{
margin-bottom: 20px;
}
.error-msg {
color: red;
}
做好的注册页面的外观如下图:
七、注册页面的其它常见功能
至此,我只是实现了一个简单的网站注册的功能。目前,主流的网站注册功能,如:短信验证码、图形验证码、邮箱激活用户等等功能,后续如果需要,逐步添加。