Django学习【第23篇】:利用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.get("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:隐藏了不占用空间
我们在注册的时候不用display:none,不然选择文件的那个功能就没有了,我们可以吧透明度
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,和static相似 但又不同
步骤如下:
- 首先在settings中配置:
# ============media配置=============== MEDIA_URL="/media/" #别名 MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","uploads") #具体路径
- 在url中配置
url(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_file").change(function () {
var ele_file = $(this)[0].files[0]; //当前选中的文件
var reader = new FileReader();
reader.readAsDataURL(ele_file); //对应找到打开的url
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)
}
二、具体实现注册操作
urls.py
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from django.conf.urls import url
2 from django.contrib import admin
3 from app01 import views
4 from django.conf import settings
5 from django.views.static import serve
6 urlpatterns = [
7 url(r'^admin/', admin.site.urls),
8 url(r'^login/$', views.login),
9 url(r'^index/$', views.index),
10 url(r'^get_vaildCode_img/$', views.get_vaildCode_img),
11 url(r'^log_out/$', views.log_out),
12
13 url(r'^register/$', views.register),
14 url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
15 ]
views.py
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def register(request):
2 if request.method=="GET":
3 form = RegisterForm()
4 return render(request,"register.html",{"form":form})
5 else:
6 form = RegisterForm(data=request.POST)
7 regresponse = {"user":None,"msg_errors":None}
8 if form.is_valid():
9 username = form.cleaned_data.get("username")
10 password = form.cleaned_data.get("password")
11 tel = form.cleaned_data.get("tel")
12 avatar_img = request.FILES.get("avatar_img")
13 print(">>>",username,password,tel)
14 models.UserInfo.objects.create_user(username = username,password=password,tel=tel,avatar=avatar_img)
15 regresponse["user"] = username
16 else:
17 print("form.errors",form.errors)
18 regresponse["msg_errors"]=form.errors
19 return HttpResponse(json.dumps(regresponse))
forms.py
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!usr/bin/env python
2 # -*- coding:utf-8 -*-
3 from app01 import models
4 from django.forms import Form
5 from django.forms import widgets
6 from django.forms import fields
7 from django.core.validators import ValidationError
8 from django.core.validators import RegexValidator
9 class RegisterForm(Form):
10 username = fields.CharField(
11 required=True,
12 max_length=16,
13 min_length=3,
14 error_messages={
15 "required": "用户名不能为空",
16 "max_length": "长度不能大于16",
17 "min_length": "长度不能小于3",
18 },
19 widget=widgets.TextInput({"placeholder":"请您输入用户名","class":"form-control"})
20 )
21 password = fields.CharField(
22 required=True,
23 max_length=16,
24 min_length=3,
25 error_messages={
26 "required": "密码不能为空",
27 "max_length": "长度不能大于16",
28 "min_length": "长度不能小于3",
29 },
30 widget=widgets.PasswordInput({"placeholder":"请您输入数字与字母组合的密码","class":"form-control"})
31 )
32 password_again = fields.CharField(
33 required=True,
34 max_length=16,
35 min_length=3,
36 error_messages={
37 "required": "密码不能为空",
38 "max_length": "长度不能大于16",
39 "min_length": "长度不能小于3",
40 },
41 widget=widgets.PasswordInput({"placeholder": "请您再次输入密码", "class": "form-control"})
42 )
43 email = fields.EmailField(
44 required=True,
45 error_messages={
46 "required":"邮箱不能为空",
47 "invalid":"邮箱格式有误"
48 },
49 widget = widgets.EmailInput({"placeholder": "请输入您的邮箱", "class": "form-control"})
50 )
51 tel = fields.CharField(
52 required=True,
53 max_length=11,
54 min_length=11,
55 error_messages={
56 "required":"手机号码不能为空",
57 "max_length":"长度必须是11位,请你正确输入",
58 "min_length":"长度必须是11位,请你正确输入",
59 },
60 validators=[RegexValidator("\d+","密码只能是数字")],
61 widget=widgets.TextInput({"placeholder": "请您输入你的电话,要求11位哦", "class": "form-control"})
62 )
63
64 #自定义用户名验证:局部钩子
65 def clean_username(self):
66 username = self.cleaned_data.get("username")
67 valid = models.UserInfo.objects.filter(username = username).first()
68 if valid:
69 raise ValidationError("用户名已存在")
70 return username
71 #自定义密码验证:
72 def clean_password(self):
73 password = self.cleaned_data.get("password")
74 if password.isdigit():
75 raise ValidationError("密码不能是纯数字")
76 else:
77 return password
78 #自定义全局钩子:验证两次密码是否一致
79 def clean(self):
80 if self.cleaned_data.get("password") == self.cleaned_data.get("password_again"):
81 return self.cleaned_data
82 else:
83 raise ValidationError("两次密码不一致")
template
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width">
7 <title>Title</title>
8 <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
9 <link rel="stylesheet" href="/static/css/reg.css">
10 </head>
11 <body>
12 {#导航条#}
13 <nav class="navbar navbar-inverse navbar-fixed-top">
14 <div class="container pull-left">
15 <!-- Brand and toggle get grouped for better mobile display -->
16 <div class="navbar-header">
17 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
18 data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
19 <span class="sr-only">Toggle navigation</span>
20 <span class="icon-bar"></span>
21 <span class="icon-bar"></span>
22 <span class="icon-bar"></span>
23 </button>
24 <a class="navbar-brand" href="#">博客园</a>
25 </div>
26
27 <!-- Collect the nav links, forms, and other content for toggling -->
28 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
29 <ul class="nav navbar-nav">
30 <li class="active c1"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
31 <li class="active c1"><a href="#">登录</a></li>
32 <li class="active c1"><a href="#">注册</a></li>
33 <li class="active c1"><a href="#">帮助</a></li>
34 </ul>
35 </div><!-- /.navbar-collapse -->
36 </div><!-- /.container-fluid -->
37 </nav>
38 <h2>注册新用户</h2>
39 <hr>
40 <div class="container">
41 <div class="row left">
42 <div class="col-md-6 col-md-offset-1">
43 <form action="/register/" method="post" novalidate enctype="multipart/form-data">
44 {% csrf_token %}
45 <div class="form-group">
46 <label for="password" class="control-label">用户名:</label>
47 <div>{{ form.username }}<span></span></div>
48 </div>
49 <div class="form-group">
50 <label for="password" class="control-label">密码:</label>
51 <div>{{ form.password }}<span></span></div>
52 </div>
53 <div class="form-group">
54 <label for="password" class="control-label">确认密码:</label>
55 <div>{{ form.password_again }}<span></span></div>
56 </div>
57 <div class="form-group">
58 <label for="email" class="control-label">邮箱:</label>
59 <div>{{ form.email }}<span></span></div>
60 </div>
61 <div class="form-group">
62 <label for="tel" class="control-label">手机号:</label>
63 <div>{{ form.tel }}<span></span></div>
64 </div>
65 <div class="form-group avatar">
66 <label for="avatar">头像:</label>
67 <img src="/static/image/default.png" alt="" class="avatar_img">
68 <input type="file" id="avatar" name="avatar_file" class="avatar_file">
69 </div>
70 <button type="button" class="btn btn-primary registr_btn">注册</button><span class="xxx"></span>
71 </form>
72 </div>
73 </div>
74 <div class="right">
75 <img src="/static/image/rigth.png" alt="">
76 </div>
77 </div>
78
79 <script src="/static/jquery-3.2.1.min.js"></script>
80 <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
81 <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
82 <script>
83 $(function () {
84 //给注册按钮增加事件
85 $(".registr_btn").click(function () {
86 var formData=new FormData();
87 formData.append("username",$("#id_username").val());
88 formData.append("email",$("#id_email").val());
89 formData.append("tel",$("#id_tel").val());
90 formData.append("password",$("#id_password").val());
91 formData.append("password_again",$("#id_password_again").val());
92 formData.append("avatar_img",$("#avatar")[0].files[0]);
93 console.log("=========",formData);
94
95 //先清除错误信息
96 $(".pull-right").html("");
97 $(".pull-right").parent().removeClass("has-error");
98 $.ajax({
99 url:"/register/",
100 type:"POST",
101 headers: {"X-CSRFToken": $.cookie('csrftoken')},
102 data:formData,
103 contentType:false,
104 processData:false,
105 success:function (data) {
106 {# console.log(data);#}
107 var data = JSON.parse(data);
108 if(data["user"]){ //或者也可以用data.user
109 $(".xxx").html("注册成功");
110 window.location.href="/login/";
111 }
112 else {
113 console.log(data.msg_errors); //拿到的是所有的错误信息
114 $.each(data.msg_errors,function (i,v) {
115 console.log(i,v);
116 $span = $("<span>");//创建一个span标签,方便提示错误信息的时候用
117 $span.addClass("pull-right").css("color","red"); //设置样式居右并且字体颜色为红色
118 $span.html(v[0]);//设置span里面的字体
119 $("#id_"+i).after($span).parent().addClass("has-error");//吧span标签放到每个input的后面显示并且让他的父亲变红,增加一个has-error的类
120
121 if (i=="__all__"){
122 $("#id_password_again").after($span)
123 }
124 });
125 }
126 }
127 })
128 });
129
130 //头像预览
131 $(".avatar_file").change(function () {
132 var ele_file = $(this)[0].files[0]; //当前选中的文件
133 var reader = new FileReader();
134 reader.readAsDataURL(ele_file); //对应找到打开的url
135 reader.onload=function () {
136 {# 方式一#}
137 $(".avatar_img").attr("src",this.result) ; //this.result是上面找到的url
138 {# 方式二#}
139 {# $(".avatar_img")[0].src=this.result; //设置图片属性#}
140 }
141 })
142 })
143 </script>
144 </body>
145 </html>
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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 position: relative;
25 width: 70px;
26 height: 70px;
27 }
28 .avatar_img,.avatar_file{
29 position: absolute;
30 width: 70px;
31 height: 70px;
32 top: 0;
33 left: 46px;
34 }
35 .avatar_file{
36 opacity: 0;
37 }
效果截图