基于form组件的注册 + 基于Ajax的注册
基于form组件的注册
urls.py
from django.contrib import admin
from django.urls import path
from blog import views
urlpatterns = [
path('admin/', admin.site.urls),
#注册
path('register/', views.register),
]
views.py
#注册视图函数
def register(request):
form_obj = forms.RegForm()
if request.method == "POST":
form_obj = forms.RegForm(request.POST)
#帮我做校验
if form_obj.is_valid():
#校验通过,去数据库创建一个新的用户
# 所以经过校验的数据都保存在 form_obj.cleaned_data (一个大 字典 中)
# print(form_obj.cleaned_data) #{'name': '某某某', 'pwd': '1234567', 're_pwd': '1234567'}
del form_obj.cleaned_data["re_password"] # 删除字典中的 re_pwd 因为数据库中没有这个属性
models.UserInfo.objects.create_user(**form_obj.cleaned_data)
return HttpResponse("注册成功!")
else:
print(form_obj.errors)
return HttpResponse("出错啦!")
return render(request, 'register.html',{"form_obj":form_obj})
register.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>注册</title> <meta name="viewport" content="width=device-width, initial-scale=1"> {% load static %} <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}"> <style> .container { margin-top: 100px; } {# 头像图片#} #avatar_img { width: 80px; height: 50px; } {# 隐藏选择图片的按钮#} #id_avatar { display: none; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-3 col-md-6"> <form action="" method="post" class="form-horizontal" enctype="multipart/form-data"> {% csrf_token %} {# 用户名#} <div class="form-group {% if form_obj.username.errors.0 %}has-error{% endif %}"> <label class="col-sm-2 control-label" for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label> <div class="col-sm-10"> {{ form_obj.username }} <span class="help-block">{{ form_obj.username.errors.0 }}</span> </div> </div> {# 密码#} <div class="form-group {% if form_obj.password.errors.0 %}has-error{% endif %}"> <label class="col-sm-2 control-label" for="{{ form_obj.password.id_for_label }}">{{ form_obj.password.label }}</label> <div class="col-sm-10"> {{ form_obj.password }} <span class="help-block">{{ form_obj.password.errors.0 }}</span> </div> </div> {# 确认密码#} <div class="form-group {% if form_obj.re_password.errors.0 %}has-error{% endif %}"> <label class="col-sm-2 control-label" for="{{ form_obj.re_password.id_for_label }}">{{ form_obj.re_password.label }}</label> <div class="col-sm-10"> {{ form_obj.re_password }} <span class="help-block">{{ form_obj.re_password.errors.0 }}</span> </div> </div> {# 邮箱#} <div class="form-group {% if form_obj.email.errors.0 %}has-error{% endif %}"> <label class="col-sm-2 control-label" for="{{ form_obj.email.id_for_label }}">{{ form_obj.email.label }}</label> <div class="col-sm-10"> {{ form_obj.email }} <span class="help-block">{{ form_obj.email.errors.0 }}</span> </div> </div> {# 头像 因为在forms.py中没有这个input选项,只能自己写#} <div class="form-group"> <label class="col-sm-2 control-label">头像</label> <div class="col-sm-10"> {# 实现了点击图片相当于点击 选择头像按钮#} <label for="id_avatar"><img id="avatar_img" src="/static/img/girl.png" alt=""></label> <input type="file" name="avatar" id="id_avatar"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-2"> <input type="submit" value="注册" class="btn btn-success"> </div> </div> </form> </div> </div> </div> <script src="{% static 'jquery-3.2.1.min.js' %}"></script> <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script> </body> </html>
自己写的forms.py
'''
BBS 用到的form类
'''
from django import forms
#定义一个注册的form类
class RegForm(forms.Form):
username = forms.CharField(
max_length=16,
label="用户名",
widget=forms.widgets.TextInput(
attrs={"class": "form-control"}
),
error_messages={
"max_length":"用户名最长16位",
"require":"用户名不能为空"
}
)
password = forms.CharField(
min_length=6,
label="密码",
error_messages={
"min_length": "密码至少6位",
"require": "密码不能为空"
},
widget=forms.widgets.PasswordInput(
render_value=True,
attrs={"class":"form-control"}
)
)
re_password = forms.CharField(
min_length=6,
label="确认密码",
error_messages={
"require": "确认密码不能为空"
},
widget=forms.widgets.PasswordInput(
render_value=True,
attrs={"class": "form-control"}
)
)
email = forms.EmailField(
label="邮箱",
error_messages={
"require": "邮箱不能为空"
},
widget=forms.widgets.EmailInput(
attrs={"class": "form-control"}
)
)
补充1
以上注册功能已经实现的差不多了,但是在 选择头像 时,选择的头像不会替换默认的图片
改进方法如下(一个新的知识点):
#给选择图片的input标签绑定事件 <script> //找到头像的input标签,绑定change 事件 $("#id_avatar").on("change",function () { //1.创建一个读文件的对象 var filereader = new FileReader(); //取到当前选中的头像文件 console.log(this.files[0]); //读取你选中的文件 filereader.readAsDataURL(this.files[0]); //读文件是需要事件的(js是异步的) //实现等待功能 filereader.onload = function () { //2.等上一步读完文件之后,把图片加载到img标签里面 $("#avatar_img").attr("src",filereader.result); } }) </script>
补充2
检验两次输入的密码是否正确
在forms.py中添加:
from django.core.exceptions import ValidationError
#重写全局的钩子函数,对确认密码做校验
def clean(self):
password = self.cleaned_data.get('password') #注意:一定要使用get来取值,如果直接用字典的方法取值的话,要是为空会报错的
re_password = self.cleaned_data.get('re_password')
if re_password and re_password != password:
self.add_error("re_password",ValidationError("两次密码不一致"))
else:
return self.cleaned_data
补充3:
判断新创建的用户名是否已经存在:
方法一:
#在forms.py中添加
#重写username字段的局部钩子
def clean_username(self):
# 判断新的用户名在数据库中是否存在
username = self.cleaned_data.get("username")
is_exist = models.UserInfo.objects.filter(username=username)
if is_exist:
# 表示用户名已经存在
self.add_error("username",ValidationError('用户名已存在!'))
方法二:
在register.html中添加:
//给input框绑定一个失去焦点的事件,失去焦点就校验用户名是否已被注册
$("#id_username").on("blur",function () {
//取到用户填写的值
var username = $(this).val();
//发请求
$.ajax({
url:"/check_username_exist/",
type:"get",
data:{"username":username},
success:function (arg) {
if(arg.status){
//用户名已被注册
$("#id_username").next().text(arg.msg).parent().parent().addClass("has-error");
}
}
})
})
在url.py中添加:
# 专门用来校验用户名是否已被注册的接口
path('check_username_exist/', views.check_username_exist),
在views.py中添加:
#校验用户名是否已被注册
def check_username_exist(request):
res = {"status":0,"msg":""}
username = request.GET.get("username")
is_exist = models.UserInfo.objects.filter(username=username)
if is_exist:
#用户名已被注册
res["status"] = 1
res["msg"] = "用户名已被注册!"
return JsonResponse(res)
基于Ajax的注册(重点)
其中,urls.py 和 forms.py同上
views.py
from blog import forms,models
from django.shortcuts import render,redirect,HttpResponse
#注册视图函数
def register(request):
if request.method == "POST":
res = {"status":0,"msg":""}
form_obj = forms.RegForm(request.POST)
#帮我做校验
if form_obj.is_valid():
#校验通过,去数据库创建一个新的用户
# 所以经过校验的数据都保存在 form_obj.cleaned_data (一个大 字典 中)
# print(form_obj.cleaned_data) #{'name': '某某某', 'pwd': '1234567', 're_pwd': '1234567'}
del form_obj.cleaned_data["re_password"] # 删除字典中的 re_pwd 因为数据库中没有这个属性
models.UserInfo.objects.create_user(**form_obj.cleaned_data)
res["msg"] = "/index/"
return JsonResponse(res)
else:
#print(form_obj.errors)
#如果有错误
res["status"] = 1
res["msg"] = form_obj.errors
return JsonResponse(res)
form_obj = forms.RegForm()
return render(request, 'register.html',{"form_obj":form_obj})
register.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>注册</title> <meta name="viewport" content="width=device-width, initial-scale=1"> {% load static %} <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}"> <style> .container { margin-top: 100px; } {# 头像图片#} #avatar_img { width: 80px; height: 50px; } {# 隐藏选择图片的按钮#} #id_avatar { display: none; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-3 col-md-6"> <form novalidate action="" method="post" class="form-horizontal" enctype="multipart/form-data"> {% csrf_token %} {# 用户名#} <div class="form-group"> <label class="col-sm-2 control-label" for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label> <div class="col-sm-10"> {{ form_obj.username }} <span class="help-block"></span> </div> </div> {# 密码#} <div class="form-group"> <label class="col-sm-2 control-label" for="{{ form_obj.password.id_for_label }}">{{ form_obj.password.label }}</label> <div class="col-sm-10"> {{ form_obj.password }} <span class="help-block"></span> </div> </div> {# 确认密码#} <div class="form-group"> <label class="col-sm-2 control-label" for="{{ form_obj.re_password.id_for_label }}">{{ form_obj.re_password.label }}</label> <div class="col-sm-10"> {{ form_obj.re_password }} <span class="help-block"></span> </div> </div> {# 邮箱#} <div class="form-group"> <label class="col-sm-2 control-label" for="{{ form_obj.email.id_for_label }}">{{ form_obj.email.label }}</label> <div class="col-sm-10"> {{ form_obj.email }} <span class="help-block"></span> </div> </div> {# 头像#} <div class="form-group"> <label class="col-sm-2 control-label">头像</label> <div class="col-sm-10"> {# 实现了点击图片相当于点击 选择头像按钮#} <label for="id_avatar"><img id="avatar_img" src="/static/img/girl.png" alt=""></label> <input type="file" name="avatar" id="id_avatar"> </div> </div> {# 注册按钮#} <div class="form-group"> <div class="col-sm-offset-2 col-sm-2"> <input type="button" value="注册" class="btn btn-success" id="reg_button"> </div> </div> </form> </div> </div> </div> <script src="{% static 'jquery-3.2.1.min.js' %}"></script> <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script> <script src="{% static 'setupajax.js' %}"></script> <script> //找到头像的input标签,绑定change 事件 $("#id_avatar").on("change",function () { //1.创建一个读文件的对象 var filereader = new FileReader(); //取到当前选中的头像文件 console.log(this.files[0]); //读取你选中的文件 filereader.readAsDataURL(this.files[0]); //读文件是需要事件的(js是异步的) //实现等待功能 filereader.onload = function () { //2.等上一步读完文件之后,把图片加载到img标签里面 $("#avatar_img").attr("src",filereader.result); } }); //用Ajax提交注册的数据 重点 $("#reg_button").on("click",function () { //取到用户填写的注册信息,向后端发送Ajax请求 $.ajax({ url:"/register/", type:"post", data:{ username:$("#id_username").val(), password:$("#id_password").val(), re_password:$("#id_re_password").val(), email:$("#id_email").val() }, success:function (arg) { if(arg.status){ //有错误就展示错误 console.log(arg.msg); //将报错信息填写到页面上 $.each(arg.msg,function (k,v) { {# console.log("id_"+k,v[0]);#} //console.log($("#id_"+k)) //找到对应input标签下面的 span标签 并添加内容 $("#id_"+k).next("span").text(v[0]); $("#id_"+k).parent().parent().addClass("has-error") }) }else{ //没有就跳转到指定页面 location.href = arg.msg; } } }) }); //将所有的input框绑定获取焦点的事件,并将所有的错误信息清空 $("form input").on("focus",function () { //this指的是当前点击的input标签 //同时实现两个功能 1.当input框中聚焦点时,清空错误信息 2.把框的颜色 变回原来的颜色 $(this).next().text("").parent().parent().removeClass("has-error"); }) </script> </body> </html>
补充:
但是这个还不完美,因为 头像还没有上传
补充如下:
views.py修改后
#注册视图函数
def register(request):
if request.method == "POST":
res = {"status":0,"msg":""}
form_obj = forms.RegForm(request.POST)
#帮我做校验
if form_obj.is_valid():
#校验通过,去数据库创建一个新的用户
# 所以经过校验的数据都保存在 form_obj.cleaned_data (一个大 字典 中)
# print(form_obj.cleaned_data) #{'name': '某某某', 'pwd': '1234567', 're_pwd': '1234567'}
del form_obj.cleaned_data["re_password"] # 删除字典中的 re_pwd 因为数据库中没有这个属性
# 要自己拿头像数据 必须自己去拿 头像数据并上传到数据库
avatar_img = request.FILES.get('avatar')
models.UserInfo.objects.create_user(**form_obj.cleaned_data,avatar=avatar_img)
res["msg"] = "/index/"
return JsonResponse(res)
else:
# print(form_obj)
#如果有错误
res["status"] = 1
res["msg"] = form_obj.errors
return JsonResponse(res)
form_obj = forms.RegForm()
return render(request, 'register.html',{"form_obj":form_obj})
register.html修改后
//注意:是局部修改
//用Ajax提交注册的数据
$("#reg_button").on("click",function () {
//取到用户填写的注册信息,向后端发送Ajax请求
var formData = new FormData();
formData.append("username",$("#id_username").val());
formData.append("password",$("#id_password").val());
formData.append("re_password",$("#id_re_password").val());
formData.append("email",$("#id_email").val());
formData.append("avatar",$("#id_avatar")[0].files[0]);
{# console.log(formData);#}
$.ajax({
url:"/register/",
type:"post",
{# 使用Ajax传文件必须加上下面两行数据#}
processData:false,
contentType:false,
{# 使用Ajax传文件时,data必须是 FormData 类型#}
data:formData,
success:function (arg) {
if(arg.status){
//有错误就展示错误
{# console.log(arg.msg);#}
//将报错信息填写到页面上
$.each(arg.msg,function (k,v) {
{# console.log("id_"+k,v[0]);#}
//console.log($("#id_"+k))
//找到对应input标签下面的 span标签 并添加内容
{# $("#id_"+k).next("span").text(v[0]);#}
{# $("#id_"+k).parent().parent().addClass("has-error");#}
//相当于上面两步
$("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
})
}else{
//没有就跳转到指定页面
location.href = arg.msg;
}
}
})
});
使用Ajax上传包含文件时的注意事项:
1.使用Ajax传文件必须加上下面两行数据(填写在 $.ajax({}) 中)
processData:false,
contentType:false,
2.使用Ajax传文件时,data必须是 FormData 类型
如:
//用Ajax提交注册的数据
$("#reg_button").on("click",function () {
//取到用户填写的注册信息,向后端发送Ajax请求
var formData = new FormData();
formData.append("username",$("#id_username").val());
formData.append("password",$("#id_password").val());
formData.append("re_password",$("#id_re_password").val());
formData.append("email",$("#id_email").val());
formData.append("avatar",$("#id_avatar")[0].files[0]);
{# console.log(formData);#}
$.ajax({
url:"/register/",
type:"post",
{# 使用Ajax传文件必须加上下面两行数据#}
processData:false,
contentType:false,
{# 使用Ajax传文件时,data必须是 FormData 类型#}
data:formData,
success:function (arg) {
if(arg.status){
//有错误就展示错误
{# console.log(arg.msg);#}
//将报错信息填写到页面上
$.each(arg.msg,function (k,v) {
{# console.log("id_"+k,v[0]);#}
//console.log($("#id_"+k))
//找到对应input标签下面的 span标签 并添加内容
{# $("#id_"+k).next("span").text(v[0]);#}
{# $("#id_"+k).parent().parent().addClass("has-error");#}
//相当于上面两步
$("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
})
}else{
//没有就跳转到指定页面
location.href = arg.msg;
}
}
})
});
小知识:
js中each的使用:
var obj = {"name":"小娜","age":18}
$.each(obj,function(k,v){
console.log(k,v);
})
>>> name 小娜
age 18
#要注意格式
js中$("...").on(" xxx ",function(){})中xxx的介绍:
最常见的几种使用:
1. click
在点击后立刻执行
2. focus
聚焦后立刻执行
3. blur
失去焦点后立刻执行
4. change
改变后立刻执行