1.注册页面头像预览
方式一:
var obj = $(this)[0] jQuery对象转化成DOM对象
var obj = $(this)[0].files
var obj = $(this)[0].files[0]; 获取上传文件的文件对象
<script> $(function () { bindAvatar1(); }); function bindAvatar1() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; console.log(obj); }) } </script>
方式二:
window.URL.createObjectURL
function () { [native code] }
<script>
$(function () {
bindAvatar2();
});
/*
本地ajax上传
*/
function bindAvatar2() {
$('#imgFile').change(function () {
var obj = $(this)[0].files[0]; #先创建文件对象
//ajax发送到后台,并获取路径
//img.src = 获取路径
var v = window.URL.createObjectURL(obj); #获取要上传的图片文件路径
$('#previewImg').attr('src',v); 把路径赋值给src
})
}
如果上传图片到内存立即释放,会出现报错(window.URL.revokeObjectURL(v);)
function bindAvatar2() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var v = window.URL.createObjectURL(obj); $('#previewImg').attr('src',v); window.URL.revokeObjectURL(v); }) }
function bindAvatar2() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var v = window.URL.createObjectURL(obj); $('#previewImg').attr('src',v); $('#previewImg').load(function () { window.URL.revokeObjectURL(v); }); }) }
加载完成之后释放就能正常显示了
方式三:onload事件
$(function () { bindAvatar3(); }); /* 本地ajax上传 */ function bindAvatar3() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var reader = new FileReader(); reader.readAsDataURL(obj); reader.onload = function(){ $('#previewImg').attr('src',this.result); } }) }
window.URL.createObjectURL
function () { [native code] }
做if判断(if,else if,else) 用方式二和方式三兼容性较差,但是都是上传图片到内存,减轻图片上传到服务器的压力,方式一兼容性较好。 <script> $(function () { bindAvatar(); }); /* 本地ajax上传 */ function bindAvatar() { if(window.URL.createObjectURL){ bindAvatar2(); }else if(window.FileReader){ bindAvatar3(); }else{ bindAvatar1(); } } function bindAvatar1() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; {# console.log(obj);#} //ajax发送到后台,并获取路径 //img.src = 获取路径 }) } function bindAvatar2() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var v = window.URL.createObjectURL(obj); $('#previewImg').attr('src',v); $('#previewImg').load(function () { window.URL.revokeObjectURL(v); }); }) } function bindAvatar3() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var reader = new FileReader(); {# console.log('1');#} reader.readAsDataURL(obj); reader.onload = function(e){ {# console.log('2');#} $('#previewImg').attr('src',this.result); } }) } </script>
方式四:FormData上传:
<script> $(function () { bindAvatar4(); }); /* 本地ajax上传 */ function bindAvatar4() { $('#imgFile').change(function(){ //$(this)[0] #jQuery对象变成DOM对象 //$(this)[0].files #获取上传当前文件的上传对象 //$(this)[0].files[0] #获取上传当前文件的上传对象的某个对象 var obj = $(this)[0].files[0]; console.log(obj); //ajax 发送给后台获取头像路径 var formdata = new FormData();//创建一个对象 formdata.append("file",obj); var xhr = new XMLHttpRequest(); xhr.open("POST","/register/"); xhr.send(formdata); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ var file_path = xhr.responseText; console.log(file_path); $("#previewImg").attr("src","/"+file_path) } } }) } </script>
2.注册页面
定义一个RegisterForm类和register函数:
- Form组件中通过构造方法可以封装自己想要的值 from django.forms import Form from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from app01 import models class RegisterForm(Form): username = fields.CharField( required=True, widget=widgets.TextInput( attrs={'class':'form-control','placeholder':'用户名为6-10个字符'} ), min_length=6, max_length=10, strip=True, error_messages={ 'required': '用户名不能为空', 'min_length':'用户名至少为6个字符', 'max_length':'用户名不超过10个字符', }, ) password = fields.CharField( required=True, widget=widgets.PasswordInput( attrs={'class':'form-control','placeholder':'密码为8-12个字符'} ), min_length=8, max_length=12, strip=True, validators=[ RegexValidator(r'((?=.*d))^.{8,12}$','必须包含数字'), RegexValidator(r'((?=.*[a-zA-Z]))^.{8,12}','必须包含字母'), # RegexValidator(r'((?=.*[^a-zA-Z0-9]))^.{8,12}','必须包含特殊字符'), # RegexValidator(r'^.(s){8,12}','必须包含空格'), ],#用于对密码的正则验证 error_messages={ 'required': '密码不能为空', 'min_length':'密码不能少于8个字符', 'max_length':'密码最多为12个字符!', } ) password_again = fields.CharField( required=True, widget=widgets.PasswordInput( attrs={'class':'form-control','placeholder':'密码为8-12个字符'} ), min_length=8, max_length=12, strip=True, error_messages={'required':'请再次输入密码!',} ) nickname = fields.CharField( required=True, widget=widgets.TextInput( attrs={'class':'form-control','placeholder':'请输入昵称'} ) ) email = fields.EmailField( required=True, widget=widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入邮箱'}), # strip=True, # error_messages={'required':'邮箱不能为空','invalid':'请输入正确的邮箱格式'}, ) avatar = fields.FileField(widget=widgets.FileInput(attrs={'id':'imgFile','class':'f1'})) code = fields.CharField(widget=widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入验证码'})) def clean_username(self): #对于username扩展验证,查看是否存在 username = self.cleaned_data['username'] users = models.UserInfo.objects.filter(username=username).count() if users:#如果用户名已存在 raise ValidationError('用户名已经存在!') return username def clean_email(self): #对于email的扩展验证,查看是否存在 email = self.cleaned_data['email'] email_count = models.UserInfo.objects.filter(email=email).count() if email_count: raise ValidationError('邮箱已经存在!') return email # def _clean_password(self):#验证两次输入密码是否一致 # password1 = self.cleaned_data['password'] # password2 = self.cleaned_data['password_again'] # if password1 and password2: # if password1 != password2: # raise ValidationError('您两次输入的密码不一致') def __init__(self,request,*args,**kwargs):#构造方法,传request参数 super(RegisterForm,self).__init__(*args,**kwargs)#完成原有form功能以外 self.request = request#再封装一个request def clean_code(self): input_code = self.cleaned_data['code'] session_code = self.request.session.get('code')#session取验证码 if input_code.upper() == session_code.upper():#验证相等时 return input_code# raise ValidationError('验证码错误') def clean(self): # 验证两次输入密码是否一致 p1 = self.cleaned_data.get('password') p2 = self.cleaned_data.get('password_again') if p1 == p2: # return self.cleaned_data return None # else: # raise ValidationError('密码不一致') self.add_error("password_again",ValidationError('密码不一致')) #在页面的指定字段添加错误信息 """ Django源码添加错误信息 # except ValidationError as e: # self.add_error(name, e) """ # def clean(self): # #基于form对象的验证,字段全部验证通过会调用clean函数验证 # self._clean_password()#调用函数 # p1 = self.cleaned_data['password'] # p2 = self.cleaned_data['password_again'] # return p2
def register(request): """ 用户注册 :param request: :return: """ if request.method == "GET": obj = RegisterForm(request) return render(request,'register.html',{'obj':obj}) else: #验证码 obj = RegisterForm(request,request.POST,request.FILES) if obj.is_valid(): pass else: # print(obj.errors['__all__']) # print(obj.errors[NON_FIELD_ERRORS]) """ <ul class="errorlist nonfield"><li>密码不一致</li></ul> <ul class="errorlist nonfield"><li>密码不一致</li></ul> """ #obj.errors是一个字典 # - 对于Form组件错误信息 """ { __all__: [错误1,错误2] user: [错误1,错误2] password: [错误1,错误2] } """ return render(request,'register.html',{'obj':obj})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册页面</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> <style> .register { 600px; margin: 0 auto; padding: 20px; margin-top: 80px; } .f1{ position: absolute; height: 80px; 80px; top: 0; left: 0; opacity: 0 } </style> </head> <body> <div class="register"> <h3>用户注册</h3> <form class="form-horizontal" method="POST" target="ifr" action="/register/" enctype="multipart/form-data"> {# <h3>{{ obj.non_field_errors }}</h3>#} {% csrf_token %} <div style="position: relative; 80px;height: 80px"> <img id="previewImg" style=" 80px;height: 80px" src="/static/images/default.png"> {# <input id="imgFile" class="f1" style="" type="file">#} {{ obj.avatar }} </div> <div class="form-group"> <label class="col-sm-2 control-label">用户名</label> <div class="col-sm-10"> {# <input type="text" class="form-control" id="inputEmail3" placeholder="用户名" name="user">#} {{ obj.username }}{{ obj.errors.username.0 }} </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">密码</label> <div class="col-sm-10"> {# <input type="password" class="form-control" placeholder="密码" name="pwd">#} {{ obj.password }}{{ obj.errors.password.0 }} <span class="pwd1"></span> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">确认密码</label> <div class="col-sm-10"> {# <input id="password" type="password" class="form-control" placeholder="确认密码" name="pwd2">#} {{ obj.password_again }}{{ obj.errors.password_again.0 }}{{ obj.non_field_errors }} <span class="pwd2"></span> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">昵称</label> <div class="col-sm-10"> {# <input id="password_again" type="password" class="form-control" placeholder="昵称" name="nickname">#} {{ obj.nickname }}{{ obj.errors.nickname.0 }} </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">邮箱</label> <div class="col-sm-10"> {# <input type="text" class="form-control" placeholder="邮箱" name="email">#} {{ obj.email }}{{ obj.errors.email.0 }} </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">验证码</label> <div class="col-sm-5"> {# <input type="text" class="form-control" placeholder="验证码" name="Verification Code">#} {{ obj.code }} {{ obj.errors.code.0 }} </div> <div class="col-sm-5"> <img style=" 120px;height: 30px" src="/check_code/"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" class="btn btn-default" value="注册"/> </div> </div> </form> </div> <script src="/static/jquery-3.2.1.js"></script> <script> $(function () { bindAvatar(); }); /* 本地ajax上传 */ function bindAvatar() { if (window.URL.createObjectURL) { bindAvatar2(); } else if (window.FileReader) { bindAvatar3(); } else { bindAvatar1(); } } function bindAvatar1() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; {# console.log(obj);#} //ajax发送到后台,并获取路径 //img.src = 获取路径 }) } function bindAvatar2() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var v = window.URL.createObjectURL(obj); $('#previewImg').attr('src', v); $('#previewImg').load(function () { window.URL.revokeObjectURL(v); }); }) } function bindAvatar3() { $('#imgFile').change(function () { var obj = $(this)[0].files[0]; //ajax发送到后台,并获取路径 //img.src = 获取路径 var reader = new FileReader(); {# console.log('1');#} reader.readAsDataURL(obj); reader.onload = function (e) { {# console.log('2');#} $('#previewImg').attr('src', this.result); } }) } function bindAvatar4() { $('#imgFile').change(function(){ //$(this)[0] #jQuery对象变成DOM对象 //$(this)[0].files #获取上传当前文件的上传对象 //$(this)[0].files[0] #获取上传当前文件的上传对象的某个对象 var obj = $(this)[0].files[0]; console.log(obj); //ajax 发送给后台获取头像路径 var formdata = new FormData();//创建一个对象 formdata.append("file",obj); var xhr = new XMLHttpRequest(); xhr.open("POST","/register/"); xhr.send(formdata); xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ var file_path = xhr.responseText; console.log(file_path); $("#previewImg").attr("src","/"+file_path) } }; }) } </script> </body> </html>
首先定义构造方法,使后端可以直接通过session取值,省去很多麻烦
def __init__(self,request,*args,**kwargs):#构造方法,传request参数
super(RegisterForm,self).__init__(*args,**kwargs)#完成原有form功能以外
self.request = request#再封装一个request
def register(request):
"""
用户注册
:param request:
:return:
"""
if request.method == "GET":
obj = RegisterForm(request)
return render(request,'register.html',{'obj':obj})
else:
#验证码
obj = RegisterForm(request,request.POST,request.FILES) #这里相应传一个request参数
if obj.is_valid():
pass
else:
# print(obj.errors['__all__'])
# print(obj.errors[NON_FIELD_ERRORS])
"""
<ul class="errorlist nonfield"><li>密码不一致</li></ul>
<ul class="errorlist nonfield"><li>密码不一致</li></ul>
"""
#obj.errors是一个字典
# - 对于Form组件错误信息
"""
{
__all__: [错误1,错误2]
user: [错误1,错误2]
password: [错误1,错误2]
}
"""
return render(request,'register.html',{'obj':obj})
在RegisterForm类中自定义clean_code函数
1.显示验证码错误信息
def clean_code(self):
input_code = self.cleaned_data['code']
session_code = self.request.session.get('code')#session取验证码
if input_code.upper() == session_code.upper():#验证相等时
return input_code#
raise ValidationError('验证码错误')
2.验证密码是否一致
def clean(self): # 验证两次输入密码是否一致
p1 = self.cleaned_data.get('password')
p2 = self.cleaned_data.get('password_again')
if p1 == p2:
# return self.cleaned_data
return None
# else:
# raise ValidationError('密码不一致')
self.add_error("password_again",ValidationError('密码不一致'))
前端HTML获取错误信息
{{ obj.non_field_errors }}
class RegisterForm(Form): username = fields.CharField( widget=widgets.TextInput(attrs={'class':'form-control'}) ) password = fields.CharField( widget=widgets.PasswordInput(attrs={'class':'form-control'}) ) password2 = fields.CharField( widget=widgets.PasswordInput(attrs={'class':'form-control'}) ) avatar = fields.FileField(widget=widgets.FileInput(attrs={'id':"imgSelect",'class':"f1" })) code = fields.CharField( widget=widgets.TextInput(attrs={'class':'form-control'}) ) def __init__(self,request,*args,**kwargs): super(RegisterForm,self).__init__(*args,**kwargs) self.request = request
- 对于Form组件错误信息
{
__all__: [错误1,错误2]
user: [错误1,错误2]
password: [错误1,错误2]
}
# 获取整体错误信息
- 后台
print(obj.errors['__all__'])
print(obj.errors[NON_FIELD_ERRORS])
- 模板
{{ obj.non_field_errors }}
Django源码关于form组件验证的流程:
is_valid()-->self.errors-->self.full_clean()-->{self._clean_fields() self._clean_form() self._post_clean()}
from django.core.exceptions import NON_FIELD_ERRORS--->NON_FIELD_ERRORS = '__all__'
按照源码添加错误信息
except ValidationError as e:
self.add_error(name, e)
self.add_error('字段名称',错误异常对象)
3.点击更换验证码
$('#i1').attr('src','/check_code/??')
<div class="col-sm-5">
<img onchange="changeCode(this);" id="i1" style=" 120px;height: 30px" src="/check_code/" title="点击更新图片">
</div>
添加一个onchange事件
function changeCode(ths) {
ths.src = ths.src + "?";
}