功能概要
- 基于 forms组件 和 Ajax 实现注册功能
- 注册头像
上传
、预览
- 利用Ajax错误
信息提示
局部钩子
:检查user是否已注册全局钩子
:检查两次密码是否相同- 利用表格
字段FileField
上传头像图片 - Media配置用户资源
储存文件夹
- 配置Media文件路径为可访问
权限
效果
文件目录
配置media用户上传目录
setting.py
# 配置用户上传静态文件 media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 做别名
MEDIA_URL = '/media/'
url.py
设置访问权限
# media配置 打开该路径浏览器访问权限
from django.views.static import serve
from cnblog import settings
# media配置
re_path(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
数据库FileField文件储存,重要!!
注册页面HTML
Ajax传头像图片
头像预览
(加载完再预览)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册新用户</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar_img{
margin-left: 20px;
}
#avatar{
display: none;
}
.error{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="">
{% csrf_token %}
{% for filed in form %}
<div class="form-group">
{# iled.auto_id 自动获取渲染id 就可以通过点击label焦点标签 #}
<label for="{{ filed.auto_id }}">{{ filed.label }}</label>
{{ filed }} <span class="error pull-right">{{ filed.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
{# 思路:利用label点击,引导等同于点击file按钮,再把file隐藏显示即可#}
<label for="avatar">
头像
<img id='avatar_img' width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar">
</div>
{# 后面加span 错误提示 #}
<input type="button" class="btn btn-default reg_btn" value="注册"><span class="error"></span>
</form>
</div>
</div>
</div>
</body>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script>
// 头像预览
// change事件来获取,用户选择头像
$('#avatar').change(function () {
// 获取用户选中的文件对象
var file_obj=$(this)[0].files[0];
// 获取文件对象的路径,文件阅读器
var reader=new FileReader()
// 阅读文件路径,返回到reader.result中
reader.readAsDataURL(file_obj)
// 读有延时,必须等待加载完后执行才会显示
reader.onload=function(){
// 修改img的src路径
$('#avatar_img').attr('src',reader.result)
}
})
// 基于Ajax提交数据
$('.reg_btn').click(function () {
var formData=new FormData() // 有文件对象所以用formData
formData.append('user',$('#id_user').val()); //forms渲染格式:id_标签名
formData.append('pwd',$('#id_pwd').val());
formData.append('re_pwd',$('#id_re_pwd').val());
formData.append('email',$('#id_email').val());
formData.append('avatar',$('#avatar')[0].files[0]); //固定语法取文件
formData.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val());
$.ajax({
url:'',
type:'post',
// 不去处理发送的数据
processData:false,
//不去设置Content-Type请求头
contentType:false,
data:formData,
success:function (data) {
if(data.user){
//注册成功 跳转页面
location.href='/login/'
}
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')
}
//通过id找到是那个标签出错 .next() 找到错误提示span标签,再取[0]错误提示信息
$('#id_'+field).next().html(error_list[0])
//找到对应div 利用boot样式增加class制作红色边框提示
$('#id_'+field).parent().addClass('has-error')
})
}
}
})
})
</script>
</html>
自定义的forms模块
校验语法
全局钩子
局部钩子
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(
min_length=4,
max_length=32,
label='用户名',
widget=widgets.TextInput(attrs={
'class': 'form-control',
}),
error_messages={
'required': '用户名不能为空',
'min_length': '长度不小于4',
},
)
pwd = forms.CharField(
min_length=4,
max_length=32,
label='密码',
widget=widgets.PasswordInput(attrs={
'class': 'form-control',
}),
error_messages={
'required': '不能为空',
'min_length': '长度不小于4',
},
)
re_pwd = forms.CharField(
min_length=4,
max_length=32,
label='确认密码',
widget=widgets.PasswordInput(attrs={
'class': 'form-control',
}),
error_messages={
'required': '不能为空',
'min_length': '长度不小于4',
},
)
email = forms.EmailField(
max_length=32,
label='邮箱地址',
widget=widgets.EmailInput(attrs={
'class': 'form-control',
}),
error_messages={
'required': '不能为空',
},
)
# 局部钩子---查看是否已经存在用户
def clean_user(self):
user = self.cleaned_data.get('user')
user_new = UserInfo.objects.filter(username=user).first()
if not user_new:
return user # 如果没有那么直接返回干净数据
else:
raise ValidationError('该用户已注册') # 钩子错误提示语法
# 全局钩子---判断2次密码是否输入一致
def clean(self):
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd and re_pwd: # 判断如果一个都不通过的情况
if pwd == re_pwd:
return self.cleaned_data # 成功返回干净数据
else:
raise ValidationError('两次密码不一致') # 错误信息储存到errors{'__all__':[e,]}
else:
return self.cleaned_data
视图中检测
头像上传储存技术
校验输入合法性
def register(request):
"""
注册功能
:param request:
:return:
"""
if request.method == 'POST':
form = UserForm(request.POST)
# 用json返回 ajax
response = {'user': None, 'msg': None}
if form.is_valid(): # 输入正确,开始创建
response['user'] = form.cleaned_data.get('user') # 返回干净数据
# 生产一个用户记录 必须用auth 的命令create_user创建加密的记录
user = form.cleaned_data.get('user')
pwd = form.cleaned_data.get('pwd')
email = form.cleaned_data.get('email')
avatar_obj = request.FILES.get('avatar') # 文件对象 头像图片
# 如果没有传头像那就不填,让他用默认图片
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)
else:
form = UserForm() # 模版渲染标签
return render(request, 'register.html', {'form': form})