专门写一个类来存form
用户名、密码、确认密码、邮箱 username = forms.CharField(max_length=8, min_length=3,required=True,error_massages={'max_length':'超出范围','min_length':'太短了','required':'这个必填'}, widget=widgets.TextInput(attrs={'class': 'form-control'})) """ max_length:限制最长 min_length:限制最短 required:设置这个是必须要填的 error_massages:提示错误信息 widget:渲染页面,添加一个额外属性,对某一个filed指定widget,传递一个attrs,是一个字典 """
钩子函数校验
局部钩子校验:(clean_字段名) def clean_username(self): # 先将名字取出来 username=self.cleaned_data.get('username') # 去数据库中比对该用户 user=UserInfo.objects.filter(username=username).first() if user: # 已经存在,不合理 raise ValidationError('该用户名已经存在') else: return username 全局钩子校验:(clean)校验多个用户 def clean(self): # 比较两个密码是否一致 :cleaned_data存的是校验过后的数据,是个字典 password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if password == re_password: # 合理,返回校验过后的数据 return self.cleaned_data else: # 不合理,抛出校验失败的异常 raise ValidationError('两次密码不一致!!')
在settings.py中配置静态资源的配置 STATIC_URL= '/static/' STATICFILES_DIRS=[os.path.join(BASE_DIR, 'static')] -把bootstrap的静态资源copy到static文件夹下
""" 前端页面使用的是bookstrap搭建的 <div class="container-fluid"> <div class="row"> <div class="col-md-6 col-md-offset-3"> </div> </div> </div> 可以使用for循环来将我们在form里面写的所有字段写出来,那么需要我们将数据传过来,进入views.py中,实例化一个对象,然后render的第三个参数context用来传输上下文,将数据传给前端 # 将文件导入 from blog.blog_forms import RegisterForm if request.method == 'GET': # register_form是我们写的那个类, register_form = RegisterForm() return render(request, 'register.html', context={'form': register_form}) """ {% for item in form %} <div class="form-group"> <label for="{{ item.id_for_label }}">{{ item.label }}</label> {{ item }} <span class="pull-right text-danger"></span> </div> {% endfor %} 头像要单独写出来不能写的fo循环里
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1 class="text-center">注册功能</h1> <form id="id_form"> {% for item in form %} <div class="form-group"> <label for="{{ item.id_for_label }}">{{ item.label }}</label> {{ item }} <span class="pull-right text-danger"></span> </div> {% endfor %} <div class="form-group"> <label for="id_file">头像 <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px"> </label> <input type="file" id="id_file" accept="image/*" style="display: none"> </div> <div class="form-group text-center"> {# 如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#} <input type="button" value="注册" class="btn btn-danger" id="id_submit"> <span class="text-danger"></span> </div> </form> </div> </div> </div> </body> </html>
1.给头像img标签设置一个id 2.给这个id设置一个change事件,如果图片发生了变化,那么就触发这个事件 3.借助于一个文件阅读器,它是js提供的一个类 var reader=new FileReader() 4.拿到文件对象,赋值给一个变量 <label for="id_file">头像<img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px" id="id_img"></label> <input type="file" id="id_file" accept="image/*" style="display: none"> var file = $("#id_file")[0].files[0] 5.把文件读到文件阅读器中 reader.readAsDataURL(file) 6.等读完后,把文件阅读器的内容写到img标签上 reader.onload=function (){ $('#id_img').attr('src', reader.result) }
def register(request): # get请求部分 if request.method == 'GET': register_form = RegisterForm() return render(request, 'register.html', context={'form': register_form}) # post请求部分 else: res = {'code': 100, 'msg': '注册成功'} # 取出用固话名密码,使用form校验数据,如果校验通过,存到数据库中。如果校验不通过,返回错误信息 register_form = RegisterForm(data=request.POST) # data就是校验的数据 if register_form.is_valid(): # 数据字段自己的规则,局部钩子,全局钩子都校验过后,通过了然后要存到数据库中 # 1.re_password字段不能存入数据库中,要删除掉 # cleaned_data是校验过后的数据,赋值给变量 register_data = register_form.cleaned_data # 从cleaned_data中将re_password剔除 register_data.pop('re_password') # 2.头像:如果携带了要存褚在request.FILES中 my_file = request.FILES.get('my_file') # my_file是一个对象 if my_file: # 直接把文件对象赋值给avatar后,保存成功,会把文件存放到FileField 的upload_to指定的路径下,然后该字段存放地址 # 本质 打开了空文件,把前端传入的头像存到空文件中,然后把地址存到avatar这个地段上 register_data['avatar'] = my_file # 3.存到数据库 UserInfo.objects.create_user(**register_data) return JsonResponse(res) else: res['code'] = 101 res['msg'] = '注册失败' res['errors'] = register_form.errors return JsonResponse(res)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1 class="text-center">注册功能</h1> <form id="id_form" > {% csrf_token %} {% for item in form %} <div class="form-group"> <label for="{{ item.id_for_label }}">{{ item.label }}</label> {{ item }} <span class="pull-right text-danger"></span> </div> {% endfor %} <div class="form-group"> <label for="id_file">头像 <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px" id="id_img"> </label> <input type="file" id="id_file" accept="image/*" style="display: none"> </div> <div class="form-group text-center"> {# 如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#} <input type="button" value="注册" class="btn btn-danger" id="id_submit"> <span class="text-danger"></span> </div> </form> </div> </div> </div> </body> <script> $("#id_file").change(function () { // 把当前图片,放到img标签中 // 把图片地址放到img标签上,标签就显示了图片 //$("#id_img")[0].src='https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg' // 1 id_file这个标签的图片读出来,借助于文件阅读器,js提供的一个类 var reader = new FileReader() // 2 拿到文件对象,赋值给一个变量 var file = $("#id_file")[0].files[0] // 3 把文件读到文件阅读器中 reader.readAsDataURL(file) // 4 等读完后,把文件阅读器的内容写到img标签上 //$("#id_img")[0].src=reader.result reader.onload = function () { //$("#id_img")[0].src=reader.result $('#id_img').attr('src', reader.result) } }) // 当点击注册按钮,发送ajax请求到后端的注册功能 $("#id_submit").click(function () { // 上传文件,借助于formdata对象 var formdata = new FormData() // 方式一 /* formdata.append('username',$('#id_username').val()) formdata.append('password',$('#id_username').val()) formdata.append('re_password',$('#id_username').val()) formdata.append('my_file',$('#id_file')[0].files[0]) // csrftoken也要加上 */ // 方式二:借助于form表单批量处理 var data = $("#id_form").serializeArray() console.log(data) // 使用for循环,把data中得数据,转存到formdata中 jquery的each循环 $.each(data, function (i, v) { //console.log("索引是:",i) //console.log("值是:",v) //console.log("------") formdata.append(v.name, v.value) }) // 文件单独再放进去 formdata.append('my_file', $('#id_file')[0].files[0]) // 使用ajax向后端发送请求 // 1 三种编码格式:urlencoded,form-data,json // {name:lqz,age:19}--->name=lqz&age=19 $.ajax({ url: '/register/', type: 'post', processData: false, contentType: false, data: formdata, success: function (data) { console.log(data) if(data.code==100){ // 表示注册成功,跳转到登录页面 location.href='/login/' }else { // 在前端显示错误信息 } } }) }) </script> </html>
# 咱们写项目后台静态资源中得图片,一般放在static文件夹下 # 用户上传的文件,图片等,一般放在media文件夹下 -我们想做的是,avatar文件夹要在media文件夹下 # django中只需要在配置文件中加一句话 # 以后再上传的文件路径是从media文件夹下开始 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
$("#id_submit").click(function () { // 上传文件,借助于formdata对象 var formdata = new FormData() // 方式一 /* formdata.append('username',$('#id_username').val()) formdata.append('password',$('#id_username').val()) formdata.append('re_password',$('#id_username').val()) formdata.append('my_file',$('#id_file')[0].files[0]) // csrftoken也要加上 */ // 方式二:借助于form表单批量处理 var data = $("#id_form").serializeArray() console.log(data) // 使用for循环,把data中得数据,转存到formdata中 jquery的each循环 $.each(data, function (i, v) { //console.log("索引是:",i) //console.log("值是:",v) //console.log("------") formdata.append(v.name, v.value) }) // 文件单独再放进去 formdata.append('my_file', $('#id_file')[0].files[0]) // 使用ajax向后端发送请求 // 1 三种编码格式:urlencoded,form-data,json // {name:lqz,age:19}--->name=lqz&age=19 $.ajax({ url: '/register/', type: 'post', processData: false, contentType: false, data: formdata, success: function (data) { console.log(data) if(data.code==100){ // 表示注册成功,跳转到登录页面 location.href='/login/' }else { // 在前端显示错误信息 console.log(data) // 两次密码不一致的错误渲染 /* if(data.errors['__all__']){ $(".error").html(data.errors['__all__'][0]) } */ // 其他标签的错误渲染 $.each(data.errors,function (k,v){ if (k=='__all__'){ //两次密码不一致的错误渲染 $(".error").html(v[0]) }else { // 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红 $("#id_"+k).next().html(v[0]).parent().addClass('has-error') } }) // 起一个定时任务 setTimeout(function (){ // 把所有span的文字去掉,把父div中得has-error类去掉 $('.text-danger').html("").parent().removeClass('has-error') },3000) } } }) })
后端校验接口
# 前端通过get把用户名传入,我们根据用户名查询数据库,如果用户名存在,返回存在,如果不存在,返回不存在 /check_username/?username=lqz def check_username(request): # /check_username/?username=lqz res = {'code': 100, 'msg': '用户存在'} username = request.GET.get('username') user = UserInfo.objects.filter(username=username).first() if user: # 用户存在 return JsonResponse(res) else: # 用户不存在 res['code'] = 101 res['msg'] = '用户不存在' return JsonResponse(res)
前端
// username输入框,失去焦点,触发ajax执行 $('#id_username').blur(function () { $.ajax({ url: '/check_username/?username=' + $(this).val(), type: 'get', success: function (data) { if (data.code == 100) { // 在span中插入错误信息 {#alert(data.msg)#} $('#id_username').next().html(data.msg) } } }) })