注册功能描述
使用者输入用户数据进行注册,对用户信息校验(forms组件),若用户输入数据不符合规范,系统提示用户错误信息和正确输入格式(ajax局部刷新),输入数据没有问题,将用户数据写入数据库,并跳转到登录界面。
设计注册页面
代码
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>blog</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/js/bootstrap.min.js"></script>
<style>
#avatar_img{
margin-left: 20px
}
</style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<!-- 当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
</label>
<input type="file" id="avatar" style="display:none">
</div>
<input type="button" class="btn btn-default login_btn" value="注册">
</form>
</div>
</div>
</div>
</body>
</html>
后端代码
from django import forms
from django.forms import widgets
class UserForm(forms.Form):
user = forms.CharField(max_length=32,
label='用户名',
widget=widgets.TextInput(attrs={'class':'form-control'})
)
pwd = forms.CharField(max_length=32,
label='密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
re_pwd = forms.CharField(max_length=32,
label='确认密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(max_length=32,
label='邮箱',
widget=widgets.EmailInput(attrs={'class': 'form-control'}))
def register(request):
form = UserForm()
return render(request, 'register.html', {'form': form})
知识点
forms组件的前端渲染
label 标签for属性的功能实现头像点击上传
效果预览
注册界面头像预览功能
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>blog</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/js/bootstrap.min.js"></script>
<style>
#avatar_img{
margin-left: 20px
}
</style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<!-- 当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
</label>
<input type="file" id="avatar" style="display:none" onload="/static">
</div>
<input type="button" class="btn btn-default login_btn" value="注册">
</form>
</div>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script>
$('#avatar').change(function (){
// 获取用户选中的文件对象
var file_obj = $(this)[0].files[0]
// 获取文件对象的路径
var reader = new FileReader()
reader.readAsDataURL(file_obj)
reader.result
// 修改img的src属性
reader.onload=function(){
$('#avatar_img').attr("src", reader.result)
}
})
</script>
</body>
</html>
知识点
ajax 的change事件
ajax 的onload事件
ajax数据提交
代码
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>blog</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/js/bootstrap.min.js"></script>
<style>
#avatar_img{
margin-left: 20px
}
</style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<!-- 当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
</label>
<input type="file" id="avatar" style="display:none" onload="/static">
</div>
<input type="button" class="btn btn-default register_btn" value="注册">
</form>
</div>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script>
$('#avatar').change(function (){
// 获取用户选中的文件对象
var file_obj = $(this)[0].files[0]
// 获取文件对象的路径
var reader = new FileReader()
reader.readAsDataURL(file_obj)
reader.result
// 修改img的src属性
reader.onload=function(){
$('#avatar_img').attr("src", reader.result)
}
})
$('.register_btn').click(function (){
// var form = new FormData()
// form.append('user', $('#id_user').val())
// form.append('pwd', $('#id_pwd').val())
// form.append('re_pwd', $('#id_re_pwd').val())
// form.append('email', $('#id_email').val())
// form.append('avatar', $('#avatar')[0].files[0])
// form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())
// 优化数据封装进FormData中的方式,优化代码冗余
var form = new FormData();
var request_data = $('#form').serializeArray();
// 循环处理
$.each(request_data, function(index, data){
form.append(data.name, data.value)
});
form.append('avatar', $('#avatar')[0].files[0])
$.ajax({
url:"",
method:"post",
data:form,
contentType:false,
processData:false,
success: function(data){
console.log(data)
}
})
})
</script>
</body>
</html>
后端代码
def register(request):
form = UserForm()
if request.is_ajax():
form = UserForm(request.POST)
response = {'user': None, 'msg': None}
if form.is_valid():
pass
else:
response['msg'] = form.errors
return JsonResponse(response)
return render(request, 'register.html', {'form': form})
知识点
ajax 提交formdata类型数据,拓展到前后端传输的三种编码格式。
ajax 提交文件数据
ajax 显示错误提示信息
在后端返回后,ajax的success函数中进行判断如果用户有输入错误信息,对其进行显示到对应标签的下方。对错误信息有误的input框使其飘红。
代码
在forms组件前端渲染的input后添加span占位标签,并为其增加样式
<head>
<meta charset="UTF-8">
<title>blog</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/js/bootstrap.min.js"></script>
<style>
#avatar_img{
margin-left: 20px
}
.error{
color: red
}
</style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<!-- 当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }} <span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
</label>
<input type="file" id="avatar" style="display:none" onload="/static">
</div>
<input type="button" class="btn btn-default register_btn" value="注册">
</form>
</div>
</div>
</div>
ajax对内容进行显示和标签渲染
<script src="/static/js/jquery.min.js"></script>
<script>
$('.register_btn').click(function (){
// var form = new FormData()
// form.append('user', $('#id_user').val())
// form.append('pwd', $('#id_pwd').val())
// form.append('re_pwd', $('#id_re_pwd').val())
// form.append('email', $('#id_email').val())
// form.append('avatar', $('#avatar')[0].files[0])
// form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())
// 优化数据封装进FormData中的方式,优化代码冗余
var form = new FormData();
var request_data = $('#form').serializeArray();
// 循环处理
$.each(request_data, function(index, data){
form.append(data.name, data.value)
});
form.append('avatar', $('#avatar')[0].files[0])
$.ajax({
url:"",
method:"post",
data:form,
contentType:false,
processData:false,
success: function(data){
// console.log(data)
if (data.user){
// 用户登录成功
}
else {
// 注册失败,错误信息处理
// 清空错误信息
$("span.error").html("")
$(".form-group").removeClass("has-error")
// 展现错误信息
$.each(data.msg, function(field, error_list){
// 找到对应input标签的下一个span标签
$('#id_'+field).next().html(error_list[0])
$('#id_'+field).parent().addClass("has-error")
})
}
}
})
})
</script>
效果展示
forms 组件对用户数据添加校验规则,全局钩子,局部钩子
代码
前端代码,在ajax中添加对全局钩子错误信息显示的特殊处理
<script src="/static/js/jquery.min.js"></script>
<script>
$('.register_btn').click(function (){
// var form = new FormData()
// form.append('user', $('#id_user').val())
// form.append('pwd', $('#id_pwd').val())
// form.append('re_pwd', $('#id_re_pwd').val())
// form.append('email', $('#id_email').val())
// form.append('avatar', $('#avatar')[0].files[0])
// form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())
// 优化数据封装进FormData中的方式,优化代码冗余
var form = new FormData();
var request_data = $('#form').serializeArray();
// 循环处理
$.each(request_data, function(index, data){
form.append(data.name, data.value)
});
form.append('avatar', $('#avatar')[0].files[0])
$.ajax({
url:"",
method:"post",
data:form,
contentType:false,
processData:false,
success: function(data){
// console.log(data)
if (data.user){
// 用户登录成功
}
else {
// 注册失败,错误信息处理
// 清空错误信息
$("span.error").html("")
$(".form-group").removeClass("has-error")
// 展现错误信息
$.each(data.msg, function(field, error_list){
// 全局钩子错误信息处理
if (field == "__all__"){
$("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error")
}
// 找到对应input标签的下一个span标签
$('#id_'+field).next().html(error_list[0])
$('#id_'+field).parent().addClass("has-error")
})
}
}
})
})
</script>
后端代码,添加全局钩子和局部钩子。另forms组件内建议放在app下的Myforms.py文件中,而不是views.py文件中
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from blog.models import UserInfo
class UserForm(forms.Form):
user = forms.CharField(max_length=32,
label='用户名',
error_messages={'required': "字段不能为空"},
widget=widgets.TextInput(attrs={'class':'form-control'})
)
pwd = forms.CharField(max_length=32,
label='密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
re_pwd = forms.CharField(max_length=32,
label='确认密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(max_length=32,
label='邮箱',
widget=widgets.EmailInput(attrs={'class': 'form-control'}))
def clean_user(self):
user = self.cleaned_data.get('user')
user_obj = UserInfo.objects.filter(username=user).first()
if not user_obj:
return user
else:
raise ValidationError("该用户已注册!")
def clean(self):
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if re_pwd == pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
知识点
forms组件局部钩子,全局钩子
效果展示
用户上传头像保存,生成用户数据
django有两种静态文件
- /static/ 是js,css,img 系统用到的静态文件和第三方框架
- /media/ 是用户上传的文件
media配置
配置MEDIA_ROOT
在settings.py中配置 MEDIA_ROOT=os.path.join(BASE_DIR, "media")
,一旦对其进行了配置,只要执行:
avatar_obj = request.FILES.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=password,email=email,avatar=avatar_obj)
django会将文件对象下载到MEDIA_ROOT中的avatar文件中(如果没有avatar文件夹,django会自动创建),user_obj的avatar必须接收的是一个文件对象,其在数据库存储的是文件的相对路径。
配置MEDIA_URL
浏览器可以直接访问到media中的数据,即为media资源开设访问接口。
在settings.py 中配置MEDIA_URL=/media/
在全局路由urls.py中添加media配置:
from django.views.static import serve
from cnblog import settings
url(r'media/(?P<path>.*)$', serve, {"document_root":settings.MEDIA_ROOT})
代码
前端代码:增加注册成功后跳转到登录界面
location.href="/login/"
后端代码:
def register(request):
form = UserForm()
if request.is_ajax():
form = UserForm(request.POST)
response = {'user': None, 'msg': None}
if form.is_valid():
# 生成一条用户记录
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
response['user'] = user
# 检测用户是否上传了头像,如果没有使用默认的
# if avatar_obj:
# user_obj = UserInfo.objects.create_user(
# username=user,
# password=pwd,
# email=email,
# avatar=avatar_obj
# )
# else:
# user_obj = UserInfo.objects.create_user(
# username=user,
# password=pwd,
# email=email
# )
# 用户存储数据代码优化
extra = {}
if avatar_obj:
extra['avatar'] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
response['msg'] = form.errors
return JsonResponse(response)
return render(request, 'register.html', {'form': form})