1 配置url路由
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('get_validCode_img/', views.get_validCode_img),
path('register/', views.register), # 新添加的
# midea配置
re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}) # 新添加的,为了用户可以直接访问其头像图片
]
2 settings.py
在settings.py中添加以下配置, 为了用户可以直接访问其头像图片
MEDIA_URL = "/media/" # 类似于STATIC_URL,django内部并未对其做路由配置,需开发人员在urls.py中做路由配置
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
3 register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar_img{
margin-left: 20px;
}
#avatar{
display:none
}
.error{
color:red
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-3">
<form id="form"> <!-- 此处用ajax提交表单,因此不需要写action属性 -->
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}<span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60" height="60" src="/static/blog/img/head-portrait.jpeg" alt="">
</label>
<input type="file" id="avatar">
</div>
<!-- 此处用ajax提交,因此使用普通按钮,不需要type="submit"的form表单指定按钮 -->
<input type="button" class="btn btn-default reg_btn" value="submit"><span class="error"></span>
</form>
</div>
</div>
</div>
<script src="/static/js/jquery-3.6.0.min.js"></script>
<script>
$("#avatar").change(function () { // jquery对象事件操作
// 获取用户选中的文件对象
var file_obj = $(this)[0].files[0];
// 获取文件对象的路径
var reader = new FileReader();
reader.readAsDataURL(file_obj); // 异步
// 修改img的src,src=文件对象的路径
reader.onload = function(){ // js对象事件操作
$("#avatar_img").attr("src", reader.result)
}
})
//基于Ajax提交数据
$(".reg_btn").click(function () {
var formdata = new FormData(); // 当提交内容有文件使需要使用form-data方式
// console.log($("#form").serializeArray());
var request_data = $("#form").serializeArray();
$.each(request_data, function(index, data){
formdata.append(data.name, data.value)
});
// 该部分注释掉的代码与68-71行的代码功能相同
// formdata.append("user", $("#id_user").val());
// formdata.append("pwd", $("#id_pwd").val());
// formdata.append("re_pwd", $("#id_re_pwd").val());
// formdata.append("email", $("#id_email").val());
// formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
formdata.append("avatar", $("#avatar")[0].files[0]); // 添加头像的文件对象
//console.log($("#avatar")[0].files[0]);
$.ajax({
url:"",
type:"post",
contentType:false, // form-data方式必须配上该参数
processData:false, // form-data方式必须配上该参数
data:formdata,
success:function (data) {
// console.log(data)
if(data.user){ // 判断是否注册成功
// 注册成功跳转到登陆页面
location.href = "/login";
}
else{ // 注册失败
// console.log(data.msg)
// 清空上次错误信息
$("span").html("");
// 清空上次错误的相应的input边框的标红
$(".form-group").removeClass("has-error");
// 展示此次提交响应的错误信息
$.each(data.msg, function (field, error_list) {
// console.log(field, error_list);
if (field=="__all__"){
$("#id_re_pwd").next().html(error_list[0]).parent().addClass();
}
// 例如:先找到id=id_user的input标签再找它的下一个span标签
// 此处使用html()或text()都可以
$("#id_"+field).next().html(error_list[0]);
// 使相应的input边框变红,bootstrap提供了has-error
$("#id_"+field).parent().addClass("has-error");
})
}
}
})
})
</script>
</body>
</html>
4 myForm.py
from django import forms
from django.forms import widgets
from blog.models import UserInfo
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class UserForm(forms.Form):
user = forms.CharField(max_length=32,
label="用户名",
error_messages={"required": "用户名不能为空!"}, # error_messages有很多不同字段,代表不同错误
widget=widgets.TextInput(attrs={"class": "form-control"})
)
pwd = forms.CharField(max_length=32,
error_messages={"required": "密码不能为空!"},
label="密码",
widget=widgets.PasswordInput(attrs={"class": "form-control"})
)
re_pwd = forms.CharField(max_length=32,
error_messages={"required": "确认密码不能为空!"},
label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"})
)
email = forms.EmailField(max_length=32,
error_messages={"required": "邮箱不能为空!",
"invalid": "邮箱地址格式错误或不支持!",
},
label="邮箱",
widget=widgets.EmailInput(attrs={"class": "form-control"})
)
def clean_user(self):
"""
Form中字段中定义的格式匹配完之后,执行此方法进行验证
:return:
"""
user = self.cleaned_data.get("user") # cleaned_data已经经过字段中定义的验证
user = UserInfo.objects.filter(username=user).first() # 从UserInfo表中查找是否有该用户
if not user:
return self.cleaned_data.get("user") # 局部钩子时返回self.cleaned_data.get("user")
else:
raise ValidationError("该用户已注册!") # 若用户已注册,需要返回的错误信息提示
def clean(self):
"""
全局钩子
:return:
"""
pwd = self.cleaned_data.get("pwd")
re_pwd = self.cleaned_data.get("re_pwd")
if pwd and re_pwd: # 避免 pwd 和 re_pwd 一个有另一个没有时,页面密码和确认密码后会提出两个不同的错误信息
if pwd == re_pwd:
return self.cleaned_data # 全局钩子时返回self.cleaned_data
else:
raise ValidationError("确认密码与密码输入不一致!")
else:
return self.cleaned_data
5 编写register视图函数
在views.py中添加以下视图函数
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.contrib import auth
from blog.models import UserInfo
from blog.myForms import UserForm
def register(request):
"""
用户注册验证
:param request:
:return:
"""
if request.is_ajax():
# print(request.POST)
# form组件功能2:检查字段的有效性,UserForm中的字段名必须与POST中的键名相同
form = UserForm(request.POST) # 创建一个form表单对象
response = {"user": None, "msg": None}
if form.is_valid(): # 检查form表单字段
response["user"] = form.cleaned_data.get("user")
# print("user", response["user"])
# 生成一条用户记录
user = form.cleaned_data.get("user")
# print("user", user)
pwd = form.cleaned_data.get("pwd")
# print("pwd", form.cleaned_data.get("pwd"))
email = form.cleaned_data.get("email")
# print("email", email)
avatar_obj = request.FILES.get("avatar") # 通过request取文件
# print(type(avatar_obj))
# print("avatar_obj", avatar_obj)
# if avatar_obj: # 判断用户是否上传头像
# # 用户上传了头像
# user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
# else: # 用户未上传头像,此时avatar_obj=None,使用默认的头像即可
# user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
# 此段代码是对75-79代码的优化
extra = {}
if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors # 已经过滤掉了csrfmiddlewaretoken和avatar
# print(response)
return JsonResponse(response)
form = UserForm() # form组件功能1:生成form表单
return render(request, "register.html", {"form": form})
6 总结
后端任务:接受前端发送来的注册信息并对其各个字段进行验证。验证通过创建一个用户,验证不通过,给前端以json形式返回错误信息。
后端任务:通过ajax给后端发送注册信息。并对接受到的响应进行处理,若返回的是成功注册信息,则直接跳转到登录页面;若返回的是错误信息,则将错误信息渲染到页面,以提示注册人员。