Django Forms 实现用户注册
分析:用户注册的信息包含了一个用户的帐号、密码、邮箱以及一个图片性质的头像。
实现文件上传的相关配置
用户模型的设计models.py
<wiz_code_mirror>
9
1
class UserInfo(AbstractUser):
2
"""继承AbstractUser表并向里加几条数据"""
3
phone = models.CharField(max_length=11, null=True, unique=True, verbose_name="手机号码")
4
avatar = models.FileField(upload_to="media/avatars", default="static/img/touxiang.jpg", verbose_name="头像")
5
create_time = models.DateTimeField(auto_now_add=True, verbose_name="用户角色创建时间")
6
7
class Meta:
8
verbose_name = "用户"
9
verbose_name_plural = verbose_name
settings.py
<wiz_code_mirror>
5
1
MEDIA_URL = '/media/'
2
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
3
4
# 继承auth模块
5
AUTH_USER_MODEL = "blog.UserInfo"
<form>表单提交实现用户上传文件功能
简单的文件上传加写入
html:
<wiz_code_mirror>
5
1
<form action="{% url 'register' %}" method="post" enctype="multipart/form-data">
2
{% csrf_token %}
3
<input type="file" name="file_obj">
4
<input type="submit">
5
</form>
注意这里文件上传的两要素:
1、form标签里面的属性enctype="multipart/form-data"
2、input type="file" name="file_obj"

后端接收、写入:
<wiz_code_mirror>
19
1
def register(request):
2
if request.method == "POST":
3
value = request.FILES.get("file_obj")
4
with open(value.name, 'wb') as f: # value.name 给文件起的名字
5
for line in value:
6
f.write(line)
7
return HttpResponse("上传成功")
8
return render(request, 'register.html')
9
10
---------------用django提供的chunks方法-----------------
11
12
def register(request):
13
if request.method == "POST":
14
value = request.FILES.get("file_obj")
15
with open(value.name, 'wb') as f:
16
for chunks in request.FILES.get('file_obj').chunks():
17
f.write(chunks)
18
return HttpResponse("上传成功")
19
return render(request, 'register.html')

用户头像文件的上传与实时预览

<wiz_code_mirror>
13
1
<form class="form-horizontal" method="post" action="{% url 'register' %}" novalidate enctype="multipart/form-data">
2
{% csrf_token %}
3
<div class="form-group">
4
<label class="col-sm-2 control-label">头像</label>
5
<div class="col-sm-10">
6
<label for="avatar">
7
<img src="{% static '/img/touxiang.jpg' %}" id="avatar-img">
8
</label>
9
<input type="file" id="avatar" style="display: none" name="file_obj">
10
</div>
11
</div>
12
<input type="submit" class="btn btn-default">
13
</form>

点击头像就能选择要上传的头像了,但是这里并不能实时预览,所以要用JS将选择的图片放到头像位置:详见代码
<wiz_code_mirror>
13
1
// 头像预览功能实现
2
// 绑定一个change事件 仅适用于文本域(text field),以及 textarea 和 select 元素。
3
$("#avatar").change(function () {
4
// 1.创建一个文件读取对象(必须的)
5
var fileReader = new FileReader();
6
// 取到当前选中的头像文件为一个对象,所以取值要加0
7
// console.log(this.files[0]);
8
fileReader.readAsDataURL(this.files[0]);
9
fileReader.onload = function () {
10
// 2.等读取完文件之后在把图片加载到img标签中
11
$("#avatar-img").attr("src", fileReader.result);
12
};
13
});
DjangoForm 字段 forms.py的设计
<wiz_code_mirror>
85
1
import re
2
from django import forms
3
from django.core.exceptions import ValidationError
4
from app01.models import *
5
6
7
def mobile_validate(value):
8
"""自定义手机号码校验规则"""
9
# re.compile()
10
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
11
# .match()
12
if not mobile_re.match(value):
13
raise ValidationError('手机号码格式错误')
14
15
16
class RegForm(forms.Form):
17
username = forms.CharField(
18
max_length=16,
19
min_length=8,
20
label="用户名",
21
error_messages={
22
"required": "用户名不能为空",
23
"min_length": "用户名不能小于8位",
24
"max_length": "用户名不能查过16位",
25
},
26
widget=forms.widgets.TextInput(
27
attrs={"class": "form-control"}
28
)
29
)
30
password = forms.CharField(
31
max_length=16,
32
min_length=8,
33
label="密码",
34
error_messages={
35
"required": "用户名不能为空",
36
"min_length": "用户名不能小于8位",
37
"max_length": "用户名不能查过16位",
38
},
39
widget=forms.widgets.PasswordInput(
40
attrs={"class": "form-control"}
41
)
42
)
43
re_password = forms.CharField(
44
max_length=16,
45
label="确认密码",
46
error_messages={
47
"required": "密码不能为空",
48
"max_length": "密码不能超过16位",
49
},
50
widget=forms.widgets.PasswordInput(attrs={
51
"class": "form-control",
52
}),
53
)
54
mobile = forms.CharField(validators=[mobile_validate, ],
55
label="手机号",
56
error_messages={'required': '手机不能为空'},
57
widget=forms.widgets.TextInput(attrs={'class': "form-control",
58
'placeholder': u'手机号码'}))
59
60
# email = forms.CharField(
61
# label="邮箱",
62
# error_messages={
63
# 'required': '邮箱不能为空'
64
# },
65
# widget=forms.widgets.EmailInput(attrs={
66
# "class": "form-control",
67
# }),
68
# )
69
70
def clean_username(self):
71
username = self.cleaned_data.get("username", "")
72
if UserInfo.objects.filter(username=username).exists():
73
raise forms.ValidationError("用户名已经存在!")
74
return username
75
76
# 在判断一个邮箱是否已存在之类的。。。代码与上面的一致
77
78
def clean_re_password(self):
79
password = self.cleaned_data.get("password", "")
80
re_password = self.cleaned_data.get("re_password", "")
81
if password != re_password:
82
raise forms.ValidationError("两次输入的密码不一致")
83
# 再往下走的话就是捕获错误异常
84
# 添加异常给errors
85
return re_password
后端代码
<wiz_code_mirror>
21
1
def register(request):
2
if request.method == "POST":
3
reg_form = RegForm(request.POST)
4
if reg_form.is_valid():
5
# 验证通过
6
print(reg_form.cleaned_data)
7
reg_form.cleaned_data.pop("re_password")
8
mobile = reg_form.cleaned_data.get("mobile", None)
9
username = reg_form.cleaned_data.get("username", None)
10
password = reg_form.cleaned_data.get("password", None)
11
avatar_img = request.FILES.get("head_logo", None)
12
UserInfo.objects.create_user(username=username, avatar=avatar_img, phone=mobile, password=password)
13
# 这个时候就要发送邮箱验证码提醒用户激活帐号!
14
return HttpResponse('恭喜您注册成功!')
15
# else:
16
# 存在异常, 这里存在异常的话前端直接可以 reg_form.mobile.errors.0 取到,else下面的语句不用执行
17
else:
18
reg_form = RegForm()
19
context = {}
20
context['reg_form'] = reg_form
21
return render(request, 'register.html', context)
完整的注册代码 前端

<wiz_code_mirror>
77
1
<!DOCTYPE html>
2
{% load staticfiles %}
3
<html lang="en">
4
<head>
5
<meta charset="UTF-8">
6
<title>Title</title>
7
<link rel="stylesheet" href="{% static "bootstrap-3.3.7-dist/css/bootstrap.min.css" %}">
8
<link rel="stylesheet" href="{% static 'my-style.css' %}">
9
<script src="{% static 'jquery-1.12.4.min.js' %}"></script>
10
<script src="{% static "bootstrap-3.3.7-dist/js/bootstrap.min.js" %}"></script>
11
<style>
12
.panel {
13
margin-top: 50px;
14
}
15
</style>
16
17
</head>
18
<body>
19
<div class="container">
20
<div class="row">
21
<div class="col-xs-6 col-xs-offset-3">
22
<!-- 表单 -->
23
<div class="panel panel-default">
24
<div class="panel-body">
25
<form class="form-horizontal" method="post" novalidate enctype="multipart/form-data">
26
{% csrf_token %}
27
28
<div class="form-group form">
29
<label for="{{ reg_form.username.id_for_label }}"
30
class="col-sm-2 control-label">{{ reg_form.username.label }}</label>
31
<div class="col-sm-10">
32
{{ reg_form.username }}
33
<span class="has-error">{{ reg_form.username.errors.0 }}</span>
34
</div>
35
</div>
36
... ... ...
37
<div class="form-group form">
38
<label class="col-sm-2 control-label">头像</label>
39
<div class="col-sm-10">
40
<label for="avatar">
41
<img src="/static/img/touxiang.jpg/" id="avatar-img">
42
</label>
43
<input type="file" id="avatar" style="display: none" name="head_logo">
44
</div>
45
</div>
46
47
<div class="form-group">
48
<div class="col-sm-12">
49
<input type="button" value="提交" class="btn btn-default pull-right" id="reg_btn">
50
</div>
51
</div>
52
53
</form>
54
</div>
55
</div>
56
</div>
57
</div>
58
</div>
59
60
<script>
61
// 头像预览功能实现
62
$("#avatar").change(function () {
63
// 1.创建一个文件读取对象
64
var fileReader = new FileReader();
65
// 取到当前选中的头像文件
66
// console.log(this.files[0]);
67
// 读取选到的对象
68
fileReader.readAsDataURL(this.files[0]);
69
// 2.等读取完文件之后在把图片加载到img标签中
70
fileReader.onload = function () {
71
$("#avatar-img").attr("src", fileReader.result);
72
};
73
});
74
</script>
75
76
</body>
77
</html>
Ajax提交文件数据
Ajax提交带文件的数据也是有坑的

<wiz_code_mirror>
1
1
formDate.append("avatar", $("#avatar")[0].files[0]); // 取input file文件里面的文件
后台接收到的是Ajax提交过来的数据,所以
<wiz_code_mirror>
1
1
avatar = request.FILES.get('avatar', None) # 这里的avatar是Ajax提交过来的数据
视图函数
<wiz_code_mirror>
25
1
def register(request):
2
if request.method == "POST":
3
reg_form = RegForm(request.POST)
4
ret = {'status': False, 'msg': ""}
5
if reg_form.is_valid():
6
# 取出清洗过的数据保存至数据库中
7
username = reg_form.cleaned_data.get('username', '')
8
# 这个密码需要加密
9
password = reg_form.cleaned_data.get('password', '')
10
mobile = reg_form.cleaned_data.get('mobile', '')
11
avatar = request.FILES.get('avatar', None) # 这里的avatar是Ajax提交过来的数据
12
print(avatar)
13
UserInfo.objects.create_user(username=username, password=password, phone=mobile, avatar=avatar)
14
# 用户注册信息保存成功返回JsonResponce
15
ret['status'] = True
16
ret['msg'] = "/login/"
17
return JsonResponse(ret)
18
else:
19
# 用户填写的字段有问题
20
ret['msg'] = reg_form.errors
21
return JsonResponse(ret)
22
23
context = {}
24
context['reg_form'] = RegForm()
25
return render(request, 'register.html', context)
前端代码
<wiz_code_mirror>
x
1
<!DOCTYPE html>
2
{% load staticfiles %}
3
<html lang="en">
4
<head>
5
<meta charset="UTF-8">
6
<title>Title</title>
7
<link rel="stylesheet" href="{% static "bootstrap-3.3.7-dist/css/bootstrap.min.css" %}">
8
<link rel="stylesheet" href="{% static 'my-style.css' %}">
9
</head>
10
<body>
11
<div class="container">
12
<div class="row">
13
<div class="col-xs-6 col-xs-offset-3">
14
<!-- 表单 -->
15
<div class="panel panel-default">
16
<div class="panel-body">
17
<form class="form-horizontal" method="post" novalidate enctype="multipart/form-data">
18
{% csrf_token %}
19
20
<div class="form-group form">
21
<label for="{{ reg_form.username.id_for_label }}"
22
class="col-sm-2 control-label">{{ reg_form.username.label }}</label>
23
<div class="col-sm-10">
24
{{ reg_form.username }}
25
<span class="has-error">{{ reg_form.username.errors.0 }}</span>
26
</div>
27
</div>
28
... ... ...
29
<div class="form-group form">
30
<label class="col-sm-2 control-label">头像</label>
31
<div class="col-sm-10">
32
<label for="avatar">
33
<img src="/static/img/touxiang.jpg/" id="avatar-img">
34
</label>
35
<input type="file" id="avatar" style="display: none" name="head_logo">
36
</div>
37
</div>
38
39
<div class="form-group">
40
<div class="col-sm-12">
41
<input type="button" value="提交" class="btn btn-default pull-right" id="reg_btn">
42
</div>
43
</div>
44
</form>
45
</div>
46
</div>
47
</div>
48
</div>
49
</div>
50
51
<script src="{% static 'jquery-1.12.4.min.js' %}"></script>
52
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
53
54
<script>
55
// 上传的头像图片实时预览
56
$("#avatar").change(function () {
57
// 新建一个文件读取对象
58
var fileReader = new FileReader();
59
// 用FileReader里面的readAsDataURL()方法取到url路径
60
fileReader.readAsDataURL(this.files[0]);
61
fileReader.onload = function () {
62
$("#avatar-img").attr("src", fileReader.result);
63
}
64
});
65
66
// Ajax提交数据给后台
67
$("#reg_btn").click(function () {
68
// 为提交按钮绑定点击事件,我们这次提交的是带图片的文件所以需特殊处理
69
var formDate = new FormData();
70
formDate.append("username", $("#id_username").val());
71
formDate.append("password", $("#id_password").val());
72
formDate.append("re_password", $("#id_re_password").val());
73
formDate.append("mobile", $("#id_mobile").val());
74
formDate.append("avatar", $("#avatar")[0].files[0]); // 取input file文件里面的文件
75
formDate.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
76
77
$.ajax({
78
url: "/register/",
79
type: 'POST',
80
// 必须加上这两句
81
processData: false,
82
contentType: false,
83
data: formDate,
84
success: function (data) {
85
console.log(data);
86
// 如果成功了,就给我跳转至某个页面,
87
if (data.status){
88
location.href = data.msg;
89
}else{
90
// 如果失败,找到具体的某个字段的错误值就将失败的信息展示出来
91
$.each(data.msg, function (k, v) {
92
// k是键,v是错误的值
93
$("#id_" + k).next("span").text(v[0]).parent().parent().addClass("has-error")
94
})
95
}
96
}
97
});
98
});
99
100
</script>
101
</body>
102
</html>
103
104
提交成功的话会自动生成文件,将提交的文件放入其中


补充知识点
JS中的FormData对象



jQuery中的for循环


加载文件的两种写法





用户注册的邮箱验证与激活
来源:https://www.cnblogs.com/pontoon/p/10216923.html