Web框架开发-Form组件和ajax实现注册
一、注册相关的知识点
1、Form组件
我们一般写Form的时候都是把它写在views视图里面,那么他和我们的视图函数也不影响,我们可以吧它单另拿出来,在应用下面建一个forms.py的文件来存放
2、局部钩子函数
# 局部钩子函数 def clean_username(self): username = self.cleaned_data.get("username") valid = models.UserInfo.objects.filter(username=username).first() if valid: raise ValidationError("用户名已存在!") return username
3、全局钩子函数
# 全局钩子函数:验证两次密码是否一致 def clean(self): if self.cleaned_data.get("password") == self.cleaned_data("password_again"): return self.cleaned_data else: raise ValidationError("两次密码不一致")
4、 jQuery的属性操作相关的
attr: 一个参数是获取属性的值,两个参数是设置属性值 removeAttr(属性名): 删除属性值 prop: 适应于属性的返回值是布尔类型的(单选,反选,取消的例子) removePorp: 删除属性的值
5、循环的两种方式
$.each(数组/对象,function(i,v){}) $("div").each(function(i,v){})
6、css中的三种隐藏:
1、display:none 隐藏所有内容 2、visibility:hidden 隐藏内容 3、overflow:hidden 隐藏溢出内容 三者都是用来隐藏的 区别在于: visibility:虽然隐藏了,但是被隐藏的内容依然占据这空间,这段隐藏了的内容却保留空间的位置会在网页中显示空白 display:隐藏了不占用空间
7、提交二进制数据用FormData
var formData=new FormData(); formData.append("username",$("#id_username").val()); formData.append("email",$("#id_email").val()); formData.append("tel",$("#id_tel").val()); formData.append("password",$("#id_password").val()); formData.append("password_again",$("#id_password_again").val()); formData.append("avatar_img",$("#avatar")[0].files[0]);
加上:
contentType:false processData:false
8、可以用下面的方法判断是什么请求
if request.ajax(): # 如果ajax请求 if request.method=="POST" # 如果是post请求
9、上传文件有一个固定的配置参数media
步骤如下:
- 首先在settings中配置:
# 与用户上传相关的配置 MEDIA_ROOT = os.path.join(BASE_DIR, "media") # 具体路径 MEDIA_URL = "/media/" # 别名
- 在路由里面配置:
# media配置: re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
用处:
用处一: ----- avatar = models.FileField(verbose_name='头像', upload_to='avatar', default="/avatar/default.png") 会把接收的文件放在media指代的路径与upload_to的拼接:BASE_DIR+blog+media+uploads+avatar/a.png avatar字段在数据库中保存的是:avatar/a.png 用处二: ------ <img src="/media/avatar/a.png">
图片上传成功保存的目录
10、头像图片预览
// 头像预览 $("#avatar").change(function () { var ele_file = $(this)[0].files[0]; // 当前选中的文件 // 获取文件对象的路径 var reader = new FileReader(); reader.readAsDataURL(ele_file); // 对应找到打开的url console.log(ele_file) // 修改img的src属性,src=文件对象的路径 reader.onload = function () { {# 方式一 #} $(".avatar_img").attr("src", this.result); // this.result上上面找到的url {# 方式二 #} {# $(".avatar_img")[0].src=this.result; //设置图片属性#} } })
11、form自动生成的错误信息
当你定义了全局钩子的时候,而且正好出现你的那个全局钩子函数中的错(比如两次密码输入不一致),这样你打印错误信息的时候
会有一个__all__对象,这个就是你设置的全局钩子生成的。
所以还要单独判断一下,现在全局钩子只有一个,你可以这样判断,但是,当全局钩子多的时候就得一个一个分开来判断
if (i=="__all__"){ $("#id_password_again").after($span) }
二、具体实现注册操作
url.py
url(r'^register/$', views.register), # media配置: re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
views.py
def register(request): if request.method == "GET": form = RegisterForm() return render(request, "register.html", {"form": form}) # return render(request, "reg.html", {"form": form}) else: form = RegisterForm(data = request.POST) regresponse = {"user": None, 'msg_errors': None} if form.is_valid(): username = form.cleaned_data.get("username") password = form.cleaned_data.get("password") tel = form.cleaned_data.get("tel") avatar_img = request.FILES.get("avatar_img") models.UserInfo.objects.create_user(username=username, password=password, tel=tel, avatar_img=avatar_img) regresponse["user"] = username else: regresponse["msg_errors"] = form.errors return HttpResponse(json.dumps(regresponse))
forms.py
class RegisterForm(Form): username = fields.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "用户名不能为空!", "max_length": "长度不能大于16", "max_length": "长度不能小于3", }, widget=widgets.TextInput({"placeholder": "请输入用户名", "class": "form-control"}) ) password = fields.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "密码不能为空!", "max_length": "长度不能大于16", "max_length": "长度不能小于3", }, widget=widgets.PasswordInput({"placeholder": "请输入数字与字母组合的密码!", "class": "form-control"}) ) password_again = fields.CharField( required=True, max_length=16, min_length=3, error_messages={ "required": "密码不能为空!", "max_length": "长度不能大于16", "max_length": "长度不能小于3", }, widget=widgets.PasswordInput({"placeholder": "请再次输入密码!", "class": "form-control"}) ) email = fields.EmailField( required=True, error_messages={ "required": "邮箱不能为空", "invalid": "邮箱格式有误" }, widget=widgets.EmailInput({"placeholder": "请输入您的邮箱", "class": "form-control"}) ) tel = fields.CharField( required=True, max_length=11, min_length=11, error_messages={ "required": "手机号码不能为空", "max_length": "长度必须是11位,请你正确输入", "min_length": "长度必须是11位,请你正确输入", }, validators=[RegexValidator("\d+", "手机号码只能是数字")], widget=widgets.TextInput({"placeholder": "请您输入你的电话,要求11位哦", "class": "form-control"}) ) # 局部钩子函数 def clean_username(self): username = self.cleaned_data.get("username") valid = models.UserInfo.objects.filter(username=username).first() if valid: raise ValidationError("用户名已存在!") return username # 全局钩子函数:验证两次密码是否一致 def clean(self): if self.cleaned_data.get("password") == self.cleaned_data("password_again"): return self.cleaned_data else: raise ValidationError("两次密码不一致")
register.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> 7 <link rel="stylesheet" href="/static/css/res.css"> 8 <script src="/static/js/jquery-3.3.1.js"></script> 9 <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> 10 <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script> 11 12 </head> 13 <body> 14 {#导航条#} 15 <nav class="navbar navbar-inverse navbar-fixed-top"> 16 <div class="container pull-left"> 17 <!-- Brand and toggle get grouped for better mobile display --> 18 <div class="navbar-header"> 19 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" 20 data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 21 <span class="sr-only">Toggle navigation</span> 22 <span class="icon-bar"></span> 23 <span class="icon-bar"></span> 24 <span class="icon-bar"></span> 25 </button> 26 <a class="navbar-brand" href="#">博客园</a> 27 </div> 28 29 <!-- Collect the nav links, forms, and other content for toggling --> 30 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 31 <ul class="nav navbar-nav"> 32 <li class="active c1"><a href="#">首页 <span class="sr-only">(current)</span></a></li> 33 <li class="active c1"><a href="#">登录</a></li> 34 <li class="active c1"><a href="#">注册</a></li> 35 <li class="active c1"><a href="#">帮助</a></li> 36 </ul> 37 </div><!-- /.navbar-collapse --> 38 </div><!-- /.container-fluid --> 39 </nav> 40 <h2>注册新用户</h2> 41 <hr> 42 <div class="container"> 43 <div class="row left"> 44 <div class="col-md-6 col-md-offset-1"> 45 <form action="/register/" method="post" novalidate enctype="multipart/form-data"> 46 {% csrf_token %} 47 <div class="form-group"> 48 <label for="username" class="control-label">用户名:</label> 49 <div>{{ form.username }}<span></span></div> 50 </div> 51 <div class="form-group"> 52 <label for="password" class="control-label">密码:</label> 53 <div>{{ form.password }}<span></span></div> 54 </div> 55 <div class="form-group"> 56 <label for="password" class="control-label">确认密码:</label> 57 <div>{{ form.password_again }}<span></span></div> 58 </div> 59 <div class="form-group"> 60 <label for="Email" class="control-label">邮箱:</label> 61 <div>{{ form.email }}<span></span></div> 62 </div> 63 <div class="form-group"> 64 <label for="tel" class="control-label">手机号码:</label> 65 <div>{{ form.tel }}<span></span></div> 66 </div> 67 <div class="form-group"> 68 <label for="avatar" class="control-label">头像:</label> 69 <img src="/static/image/default.png" alt="" class="avatar_img"> 70 <input type="file" id="avatar" name="avatar_file" class="avatar_file"> 71 </div> 72 <button type="button" class="btn btn-primary registr_btn">注册</button><span class="error"></span> 73 </form> 74 </div> 75 </div> 76 <div class="right"> 77 {# <img src="/static/image/lufei.jpg" alt="">#} 78 </div> 79 80 </div> 81 <script> 82 $(function () { 83 // 给注册按钮添加事件 84 $(".registr_btn").click(function () { 85 var formData = new FormData(); 86 formData.append("username", $("#id_username").val()); 87 formData.append("email", $("#id_email").val()); 88 formData.append("tel", $("#id_tel").val()); 89 formData.append("password", $("#id_password").val()); 90 formData.append("password_again", $("#id_password_again").val()); 91 formData.append("avatar_img", $("#avatar")[0].files[0]); 92 93 94 $(".pull-right").html(""); 95 $(".pull-right").parent().removeClass("has-error"); 96 $.ajax({ 97 url:"/register/", 98 type: "post", 99 headers: {"X-CSRFToken": $.cookie('csrftoken')}, 100 data:formData, 101 contentType:false, 102 processData:false, 103 success:function (data) { 104 105 var data = JSON.parse(data); 106 if (data["user"]){ 107 $(".error").html("注册成功") 108 window.location.href="/login/"; 109 110 } 111 else { 112 // 先清除错误信息 113 114 console.log(data.msg_errors); // 拿到的是所有的错误信息 115 $.each(data.msg_errors, function (field, error_list) { 116 console.log(field, error_list); 117 $span = $("<span>"); 118 $span.addClass("pull-right").css("color", "red"); 119 120 $span.html(error_list[0]); 121 $("#id_" + field).after($span).parent().addClass("has-error"); 122 // 错误信息5秒消失 123 setTimeout(function () { 124 $(".pull-right").text("") 125 126 }, 5000); 127 128 if (field=="__all__"){ 129 $("#id_password_again").after($span); 130 } 131 132 133 134 135 136 137 138 139 }); 140 } 141 142 } 143 }) 144 }); 145 146 // 头像预览 147 $("#avatar").change(function () { 148 var ele_file = $(this)[0].files[0]; // 当前选中的文件 149 // 获取文件对象的路径 150 var reader = new FileReader(); 151 reader.readAsDataURL(ele_file); // 对应找到打开的url 152 console.log(ele_file) 153 // 修改img的src属性,src=文件对象的路径 154 reader.onload = function () { 155 {# 方式一 #} 156 $(".avatar_img").attr("src", this.result); // this.result上上面找到的url 157 {# 方式二 #} 158 {# $(".avatar_img")[0].src=this.result; //设置图片属性#} 159 160 } 161 162 }) 163 164 165 166 }) 167 </script> 168 </body> 169 </html>
reg.css
1 .c1 { 2 margin-right: 10px; 3 } 4 5 h2 { 6 margin-top: 100px; 7 margin-left: 280px; 8 } 9 .left{ 10 position: relative; 11 } 12 .right{ 13 width: 270px; 14 height: 294px; 15 position: absolute; 16 top: 197px; 17 left: 886px; 18 } 19 .registr_btn{ 20 width: 100px; 21 margin-left: 200px; 22 } 23 .avatar{ 24 display: none; 25 } 26 .avatar_img,.avatar_file{ 27 position: absolute; 28 width: 60px; 29 height: 60px; 30 31 left: 60px; 32 } 33 .avatar_file{ 34 opacity: 0; 35 } 36 37 .error { 38 color: red; 39 margin-left: 10px; 40 }
效果截图