[1 注册功能](#1 注册功能)
[1.1 注册功能form组件](#1.1 注册功能form组件)
[1.2 注册功能前端](#1.2 注册功能前端)
[1.3 注册功能后端](#1.3 注册功能后端)
[1.4 forms组件和前后端总结](#1.4 forms组件和前后端总结)
[2 登录功能](#2 登录功能)
[2.1 登录前端](#2.1 登录前端)
[2.2 生成验证码](#2.2 生成验证码)
1 注册功能
1.1 注册功能form组件
python
# 注册页面
-用户名
-密码
-确认密码
-邮箱
-手机号
-头像
# form组件 可以帮助我们
1 快速生成前端页面
2 数据校验
3 错误处理
# 如何使用
-1 写一个类,继承forms.Form
-2 在类中写属性和方法
-属性:要跟咱们要校验或自动生成页面的字段一一对应
-方法:对字段进行校验:
clean_字段名 给单个字段校验
clean 给多个字段校验
-3 在视图函数中使用
-4 模板中使用
# form表单中,如果定义了button或input 类型是"submit",只要点击,就会默认触发form表单的提交,如果我们又写了ajax提交,就会触发两次---》导致问题-
-把它搞外面
-input 类型是"button"
python
from django import forms
from django.forms import widgets, ValidationError
from .models import UserInfo
class RegisterForm(forms.Form):
# max_length=18 最长 18
# min_length=3 最短 3
# required=True 必填
username = forms.CharField(max_length=18, min_length=3, required=True,
label='用户名',
error_messages={
'required': '用户名字段必填',
'max_length': '长度不能超过18',
'min_length': '最短3'
}, widget=widgets.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(max_length=18, min_length=3, required=True,
label='密码',
error_messages={
'required': '用户名字段必填',
'max_length': '长度不能超过18',
'min_length': '最短3'
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
re_password = forms.CharField(max_length=18, min_length=3, required=True,
label='确认密码',
error_messages={
'required': '用户名字段必填',
'max_length': '长度不能超过18',
'min_length': '最短3'
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(max_length=18, min_length=3, required=True,
label='邮箱',
error_messages={
'required': '用户名字段必填',
'max_length': '长度不能超过18',
'min_length': '最短3'
}, widget=widgets.EmailInput(attrs={'class': 'form-control'}))
phone = forms.CharField(max_length=11, min_length=11, required=True,
label='手机号',
error_messages={
'required': '用户名字段必填',
'max_length': '长度不能超过11',
'min_length': '必须11为'
}, widget=widgets.TextInput(attrs={'class': 'form-control'}))
# 方法名只能写两类
# 一类是 clean_字段名 校验单个字段
def clean_username(self): # 如果能走到这里,说明上面的校验已经通过了,校验过后的数据都放在一个字典中---》cleaned_data
username = self.cleaned_data.get('username')
# 用户名不能以sb开头
if username.startswith('sb'):
# 校验不通过,抛异常
raise ValidationError('名字不能以sb开头')
# 如果用户名存在,也不能注册了
res = UserInfo.objects.filter(username=username).exists()
if res:
raise ValidationError('该用户已经存在')
return username
# 二类是 clean 同时校验多个字段
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if not password == re_password:
raise ValidationError('两次密码不一致')
return self.cleaned_data
1.2 注册功能前端
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="text-center">注册功能</h1>
<form id="register_form">
{% csrf_token %}
{% for foo in form %}
<div class="form-group">
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }} <span class="pull-right error" style="color: red"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="id_avatar">头像
<img src="/static/img/default.png" alt="" height="80px" width="80px" id="id_img"
style="margin-left: 20px">
</label>
<input type="file" id="id_avatar" class="form-control" accept="image/*" style="display: none">
</div>
<div class="text-center">
<input type="button" value="注册" class="btn btn-danger" id="id_submit">
<span class="error" style="color: darkred;margin-left: 10px" id="id_error"></span>
</div>
</form>
</div>
</div>
</div>
</body>
<script>
// 1 监控文件变化
$('#id_avatar').change(function () {
// 读出input 的图片,写到 img标签上
// 需要借助于文件阅读器
var fileReader = new FileReader();
// 把文件对象读入文件阅读器中
fileReader.readAsDataURL($('#id_avatar')[0].files[0])
// 等文件读完,再放入
fileReader.onload = function () {
//$('#id_img').attr('height', '300px')
$('#id_img').attr('src', fileReader.result)
//$('#id_img')[0].src = fileReader.result
}
})
// 2 按钮提交---》注册功能
$('#id_submit').click(function () {
var formdata = new FormData()
// 把文件放入
formdata.append('my_img', $('#id_avatar')[0].files[0])
//放数据:用户名,密码,确认密码,手机号,邮箱 ,你可以一个个放--->笨办法
/*
formdata.append('username', $('#id_username').val())
formdata.append('password', $('#id_password').val())
formdata.append('re_password', $('#id_re_password').val())
formdata.append('phone', $('#id_phone').val())
formdata.append('email', $('#id_email').val())
formdata.append('csrfmiddlewaretoken', '{{ csrf_token }}') // csrf 的token
*/
// 简单方案
var register_form = $('#register_form').serializeArray() // 会把当前form表单中得数据放到列表套字典的形式
/*
数组
[{name:xx,value:yy}, {...}, {...}, {...}, {...}]
*/
//console.log(register_form)
// jq 的循环
$.each(register_form, function (i, v) {
//console.log(v['name'])
//console.log(v['value'])
formdata.append(v['name'], v['value'])
})
$.ajax({
url: '/register/',
method: 'post',
processData: false,
contentType: false,
data: formdata,
success: function (data) {
if (data.code == 100) {
location.href = '/login/'
} else { // 不成功
// 两次密码不一致,把错误写在 注册按钮后面
// input 自己的错误,写在自己后面
// 循环返回的错误
$.each(data.errors, function (key, value) {
if (key == '__all__') {
$('#id_error').html(value[0])
}
$('#id_' + key).next().html(value[0]).parent().addClass('has-error')
})
// 过3s后,清空错误,和红框
setTimeout(function () {
$('.error').html("").parent().removeClass('has-error')
//alert('asfdsdaf')
}, 3000)
}
}
})
})
// 3 当用户名输入框失去焦点,我们就去后端校验用户名是否注册过
$('#id_username').blur(function () {
//alert('失去焦点了')
//var username=$('#id_username').val()
var username = $(this).val()
$.ajax({
url: '/check_username/?username=' + username,
method: 'get',
success: function (data) {
console.log(data)
if (data.code != 100) {
// 1 清空输入框
//$(this).val()
// 2 错误提示
//$(this).next().html(data.msg)
console.log('ssss')
// 两句可以并做一句---》链式调用
// 如果在另一个内部函数中,就不能用this
//var ss=$('#id_username').val()
//$('#id_username').next().html(data.msg).parent().addClass('has-error').children('input').val("")
$('#id_username').val('').next().html(data.msg).parent().addClass('has-error')
}
}
})
})
</script>
</html>
1.3 注册功能后端
python
from django.shortcuts import render
from .forms import RegisterForm
from .models import UserInfo
from django.http import JsonResponse
def register(request):
if request.method == 'GET':
form = RegisterForm()
return render(request, 'register.html', {'form': form})
else:
# # 1 数据
# print(request.POST)
# # 2 文件
# print(request.FILES.get('my_img'))
# 取出头像
avatar = request.FILES.get('my_img')
# 校验数据是否合法
'''
username: admin
password: 123
email: an@qq.com
phone: 12345678935
avatar:文件
'''
form = RegisterForm(request.POST) # 使用form校验传入的数据
if form.is_valid(): # 校验通过
# 保存
data = form.cleaned_data
# 把re_password 弹出
data.pop('re_password')
# 把头像加入
if avatar:
data['avatar'] = avatar
UserInfo.objects.create_user(**data)
return JsonResponse({'code': 100, 'msg': '注册成功'})
else:
return JsonResponse({'code': 101, 'msg': '注册失败', 'errors': form.errors})
# 校验用户名是否存在的接口
def check_username(request):
username = request.GET.get('username')
res = UserInfo.objects.filter(username=username).exists()
if res:
# 约定状态码:100,表示成功,非100表示失败
return JsonResponse({'code': 101, 'msg': '用户已经存在'})
return JsonResponse({'code': 100, 'msg': '您可以注册'})
1.4 forms组件和前后端总结
python
# 1 forms组件
-1 渲染模板
-2 校验数据
-3 渲染错误
- form=RegisterForm()---渲染页面
- form=RegisterForm(requets.POST)---校验数据
# 2 取数据,取文件
-form-data提交数据
-request.POST 中取数据
-request.FILES.get('名字') 取文件
-补:前端是key:value 后端变成了 key:[value]
-request.data 不是真正的字典
{'username': ['admin','xxx'], 'password': ['123']}
request.POST.get('username')
request.POST.getlist('username')
# 3 保存 文件和数据
# data 是form校验过后的数据,没有头像
data.pop('re_password') # 不是表的字段
# 把头像加入 头像是表的字段
if avatar:
data['avatar'] = avatar # 文件对象
# avatar = models.ImageField(upload_to='avatar', default='avatar/default.png')
内部自动:打开一个空文件,把文件写入到空文件中 【/media/avatar/】,并且把路径赋值给avatar数据库字段
UserInfo.objects.create_user(**data)
# 4 前端:头像实时显示
-隐藏了 input file---》input只能接收图片类型
-只要input发生变化(change)---》把图片读出来,写入到 img标签中
var fileReader = new FileReader();
fileReader.readAsDataURL($('#id_avatar')[0].files[0])
fileReader.onload = function () {
$('#id_img').attr('src', fileReader.result)
}
# 5 用户名失去焦点(blur)---》向后端校验
# 6 form表单使用var register_form = $('#register_form').serializeArray() 转到数组中
# 7 $.each(可以被循环的,function(){})
# 8 错误信息渲染
-__all__ 全局错误---》显示在注册后面
if (key == '__all__') {
$('#id_error').html(value[0])
}
-其他错误,显示在自己后面
$('#id_' + key).next().html(value[0]).parent().addClass('has-error')
-定时任务:3s后干什么
setTimeout(function () {
$('.error').html("").parent().removeClass('has-error')
}, 3000)
2 登录功能
2.1 登录前端
python
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="text-center">登录功能</h1>
<form>
<div class="form-group">
<label for="">用户名</label>
<input type="text" name="username" class="form-control">
</div>
<div class="form-group">
<label for="">密码</label>
<input type="text" name="password" class="form-control">
</div>
<div class="form-group">
<label for="">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" name="code" class="form-control">
</div>
<img src="/get_code/" alt="" class="col-md-6" height="35">
</div>
</div>
<div class="text-center" style="margin-top: 50px">
<input type="button" value="登录" class="btn btn-danger" id="id_submit">
<span class="error" style="color: darkred;margin-left: 10px" id="id_error"></span>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
2.2 生成验证码
python
# 第三方方案
https://pythonjishu.com/ljpdvvedzkqiovs/
# 自己的方案
python
def get_code(request):
# 前端显示图片 方式一
# with open('./static/img/4.jpg', 'rb') as f:
# data = f.read()
# return HttpResponse(data)
# 方式二:自己生成一张图片,保存到本地-->打开,返回给前端
# image_tmp = Image.new('RGB', (300, 38), (255, 255, 0))
#
# with open('code.png', 'wb') as f:
# image_tmp.save(f, 'png')
#
# with open('code.png', 'rb') as f:
# data = f.read()
# return HttpResponse(data)
# 方式三:借助于ByteIo,把文件内容放在内存中
# image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
# # 放在内存中
# my_io = BytesIO()
# image_tmp.save(my_io, 'png')
# return HttpResponse(my_io.getvalue())
# 方式四: 要在图片上写文字
# image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
# # 把空图片放在了画板上
# draw = ImageDraw.Draw(image_tmp)
# draw.text((0, 0), 'lqz')
# my_io = BytesIO()
# image_tmp.save(my_io, 'png')
# return HttpResponse(my_io.getvalue())
# 方式5 :加入字体文件
# image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
# # 把空图片放在了画板上
# draw = ImageDraw.Draw(image_tmp)
# # 加入字体
# img_font = ImageFont.truetype('./static/font/xgdl.ttf', 23)
# draw.text((0, 0), '西瓜大朗', fill=(0, 0, 128), font=img_font, )
# my_io = BytesIO()
# image_tmp.save(my_io, 'png')
# return HttpResponse(my_io.getvalue())
# 方式6 ,随机生成 5 大小写字母和数字,图片背景色和字的颜色每次不一样
image_tmp = Image.new('RGB', (300, 38), (0, 255, 0))
# 把空图片放在了画板上
draw = ImageDraw.Draw(image_tmp)
# 加入字体
img_font = ImageFont.truetype('./static/font/xgdl.ttf', 23)
draw.text((0, 0), '西瓜大朗', fill=(0, 0, 128), font=img_font, )
my_io = BytesIO()
image_tmp.save(my_io, 'png')
return HttpResponse(my_io.getvalue())
import random
def get_random_code():
code = ''
for i in range(5):
# 随机生成一个大写字母
upper_char = chr(random.randint(65, 90))
low_char = chr(random.randint(97, 122))
num_char = str(random.randint(0, 9))
res = random.choice([upper_char, low_char, num_char])
code += res
return code
if __name__ == '__main__':
print(get_random_code())