04 用户注册
基于forms组件创建表单
创建forms
from django import forms from django.forms import widgets class UserInfo(forms.Form): user=forms.CharField(max_length=32,label="用户名",widget=widgets.TextInput(attrs={"class":'form-control'})) pwd=forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"})) re_pwd=forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"})) email=forms.EmailField(max_length=32,label="邮箱地址",widget=widgets.EmailInput(attrs={"class":'form-control'}))
html中渲染forms组件
<form> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}:</label>{{ field }} </div> {% endfor %} <input type="button" class="button form-control btn-success" value="提交"> </form>
添加用户头像
添加用户图像图标
添加一个img标签,连接到static下的一个default.png文件
技巧:
- 利用label的for属性,让点击图片等同于点击input标签
- 将input[type='file']的标签隐藏掉,设置display属性为none
<div class="container"> <div class="row"> <div class="col-md-4 col-lg-offset-4"> <form> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}:</label>{{ field }} </div> {% endfor %} <div class="form-group"> <label for="avatar">头像 <img class="avatar_img" src="/static/app/img/default.png" style="width: 60px;height: 60px;display: inline"> </label> <input type="file" id="avatar" style="display: none;"> </div> <input type="button" class="button form-control btn-success" value="提交"> </form> </div> </div> </div>
用户头像预览功能
- 获取用户选中的文件对象,通过input标签的change事件来触发
- 获取文件对象的本地路径,一定要等图片加载完成之后再来获取文件路径
- 修改img的src属性值
<script> $('#avatar').change(function () { //获取用户选中的文件对象 var file_obj=$(this)[0].files[0]; //获取当前文件对象路径 var reader=new FileReader();//实例化一个reader reader.readAsDataURL(file_obj);//读取文件对象路径,读取完成后放在reader自己内部 //待读取完后,获取url和修改imgsrc属性 reader.onload=function () { var file_url=reader.result;//获取文件路径url //修改img标签的src属性 $('#avatar_img').attr('src',file_url); }; }) </script>
用ajax提交formdata数据
ajax数据提交
- 上传的data为formdata,需要new一个
- contentType为false
- processData为false
方式一创建formdata的ajax代码
<script> //提交formdata数据 $('input[type="button"]').click(function () { var formdata=new FormData(); formdata.append("user",$("#id_user").val()); formdata.append("pwd",$("#id_pwd").val()); formdata.append("re_pwd",$("#id_re_user").val()); formdata.append("email",$("#id_email").val()); formdata.append("avatar",$("#avatar")[0].files[0]); formdata.append("csrfmiddlewaretoken",$("input[name='csrfmiddlewaretoken']").val()); $.ajax({ url:'', data:formdata, type:'post', contentType:false, processData:false, success:function (data) { console.log(data) }, error:function (err) { console.log(err) } }) }) </script>
方式二创建formdata
<script> //提交formdata数据 $('input[type="button"]').click(function () { var formdata=new FormData();formdata.append("csrfmiddlewaretoken",$("input[name='csrfmiddlewaretoken']").val());#} var formarray= $("#form").serializeArray(); $.each(formarray,function (index, data) { formdata.append(data.name,data.value); }); formdata.append("avatar",$("#avatar")[0].files[0]); $.ajax({ url:'', data:formdata, type:'post', contentType:false, processData:false, success:function (data) { console.log(data) }, error:function (err) { console.log(err) } }) }) </script>
views代码,可通过is_ajax()验证,也可以通过request.POST验证
def register(request): # if request.method=='POST': if request.is_ajax(): response={"user":None,"msg":None} form=UserInfoForm(request.POST) if form.is_valid(): response['user']=form.cleaned_data.get('user'); else: print(form.cleaned_data) print(form.errors) response['msg']=form.errors return JsonResponse(response)
加载error提示
- 通过jquery的each方法循环errs,每个error的index对应的和input的id有规律,其实也可以用name直接获取。查找到input标签后,通过next方法查找下一个标签
- 在将error写入span之前,需要将所有的error信息清除掉,否则第二次提交的是第一次的error信息任然
JavaScript代码
<script> //提交formdata数据 $('input[type="button"]').click(function () { var formdata=new FormData(); var formarray= $("#form").serializeArray(); $.each(formarray,function (index, data) { formdata.append(data.name,data.value); }); formdata.append("avatar",$("#avatar")[0].files[0]); $.ajax({ url:'', data:formdata, type:'post', contentType:false, processData:false, success:function (data) { if(data.msg){ //数据校验失败,导致注册失败 //动态加载错误信息 $(".error").html("");//先清空一次,避免上次错误信息任然存在 $(".form-group").removeClass('has-error'); $.each(data.msg,function (index,error_list) { $("#id_"+index).next().html(error_list[0]); $("#id_"+index).parent().addClass('has-error');//错误字段边框变红 }) }else{ //注册成功 } }, error:function (err) { console.log(err) } }) }) </script>
错误信息修改成中文提示
在form字段中,添加error_message参数
class UserInfo(forms.Form): user=forms.CharField(max_length=32,label="用户名", widget=widgets.TextInput(attrs={"class":'form-control'}), error_messages={"required":"用户名不能为空"}) pwd=forms.CharField(max_length=32,label="密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"密码不能为空"}) re_pwd=forms.CharField(max_length=32,label="确认密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"确认密码不能为空"}) email=forms.EmailField(max_length=32,label="邮箱地址", widget=widgets.EmailInput(attrs={"class":'form-control'}), error_messages={"required":"邮箱不能为空"})
钩子校验数据
局部钩子检查用户名是否已经注册
class RegisterUser(forms.Form): user=forms.CharField(max_length=32,label="用户名", widget=widgets.TextInput(attrs={"class":'form-control'}), error_messages={"required":"用户名不能为空"}) pwd=forms.CharField(max_length=32,label="密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"密码不能为空"}) re_pwd=forms.CharField(max_length=32,label="确认密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"确认密码不能为空"}) email=forms.EmailField(max_length=32,label="邮箱地址", widget=widgets.EmailInput(attrs={"class":'form-control'}), error_messages={"required":"邮箱不能为空"}) #局部钩子校验用户 def clean_user(self): val=self.cleaned_data.get('user') user=UserInfo.objects.filter(username=val).first() if not user: #返回原来的val return val else: raise ValidationError("该用户已注册")
全局钩子检查两次密码是否一致
class RegisterUser(forms.Form): user=forms.CharField(max_length=32,label="用户名", widget=widgets.TextInput(attrs={"class":'form-control'}), error_messages={"required":"用户名不能为空"}) pwd=forms.CharField(max_length=32,label="密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"密码不能为空"}) re_pwd=forms.CharField(max_length=32,label="确认密码", widget=widgets.PasswordInput(attrs={"class":"form-control"}), error_messages={"required":"确认密码不能为空"}) email=forms.EmailField(max_length=32,label="邮箱地址", widget=widgets.EmailInput(attrs={"class":'form-control'}), error_messages={"required":"邮箱不能为空"}) #全局钩子验证两次密码 def clean(self): pwd=self.cleaned_data.get('pwd') re_pwd=self.cleaned_data.get('re_pwd') if pwd and re_pwd: if pwd == re_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data
注意:全局钩子的错误放在__all__中的,所以each循环的时候判断是否是__all__即可判断是否为全局错误信息
前端JavaScript展示全局错误信息
<script> //提交formdata数据 $('input[type="button"]').click(function () { var formdata=new FormData(); var formarray= $("#form").serializeArray(); $.each(formarray,function (index, data) { formdata.append(data.name,data.value); }); formdata.append("avatar",$("#avatar")[0].files[0]); $.ajax({ url:'', data:formdata, type:'post', contentType:false, processData:false, success:function (data) { if(data.msg){ //数据校验失败,导致注册失败 //动态加载错误信息 $(".error").html("");//先清空一次,避免上次错误信息任然存在 $(".form-group").removeClass('has-error'); $.each(data.msg,function (index,error_list) { if(index == '__all__'){// $("#id_re_pwd").next().text(error_list[0]) } $("#id_"+index).next().html(error_list[0]); $("#id_"+index).parent().addClass('has-error');//错误字段边框变红 }) }else{ //注册成功 } }, error:function (err) { console.log(err) } }) }) </script>
所有数据校验通过
添加用户记录
def register(request): # if request.method=='POST': if request.is_ajax(): response={"user":None,"msg":None} form=RegisterUser(request.POST) if form.is_valid(): response['user']=form.cleaned_data.get('user') user=form.cleaned_data.get('user') pwd=form.cleaned_data.get('pwd') email=form.cleaned_data.get('email') avatar_obj=request.FILES.get('avatar') if avatar_obj: UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj) else: #用户没有上传头像则不能传入avatar参数,不传和传空是不一样的 UserInfo.objects.create_user(username=user, password=pwd, email=email) else: response['msg']=form.errors return JsonResponse(response) form=RegisterUser() return render(request,'register.html',{"form":form})
用户校验成功,前端跳转到登录界面
if (data.user){ //用户校验成功,跳转到登录界面 location.href='/login/'; }
django静态文件
django有两种静态文件:
/static/ 前端需求资源
/media/ 所有用户上传的文件都在该文件夹中
django配置media文件件配置,在setting文件中操作
一旦配置过media,那么FileField中上传的文件将放置在media中