实现页面效果
实现思路
- 使用form渲染数据
- 校验手机号(格式、是否注册)、密码以及验证码
生成图片验证码
'''
pillow:是python处理图片的模块,很强大
'''
import random
from PIL import Image, ImageDraw, ImageFont
def picture_code():
def get_char():
'''通过数字获取ASCII表中的对应字母'''
return chr(random.randint(65, 90))
def get_color():
'''获取随机颜色'''
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
# mode:色系 size:宽高 color:颜色
# RGB颜色对照表:https://tool.oschina.net/commons?type=3
img = Image.new(mode='RGB', size=(120, 50), color=(220, 220, 220))
# 创建画笔对象
draw = ImageDraw.Draw(img, mode='RGB')
# 画点 xy:基于图片的坐标,fill:颜色
for i in range(10):
draw.point([random.randint(0, 120), random.randint(0, 50)], fill='green')
# 画线 xy:(起点坐标,终点坐标),width:粗细
for i in range(5):
draw.line(
[random.randint(0, 120), random.randint(0, 50), random.randint(0, 120), random.randint(0, 50)],
fill='red'
)
# 画圆或弧线 start and end:角度
for i in range(3):
x = random.randint(0, 120)
y = random.randint(0, 50)
x2 = x + 4
y2 = y + 4
draw.arc([x, y, x2, y2], start=0, end=90, fill='red')
# 注意点:.ttf文件路径不能含有中文
font = ImageFont.truetype('fonts/domi.ttf', 35)
char_list = []
for i in range(5):
char = get_char()
char_list.append(char)
height = random.randint(10, 15)
draw.text([18 * (i + 1), height], text=char, fill='red', font=font)
char_code = ''.join(char_list)
return img, char_code
if __name__ == '__main__':
img, r = picture_code()
# 创建在内存中,这里直接生成在本地,方便自己调式查看
with open('code.png', 'wb') as f:
img.save(f, format='png') # format:格式
print(r)
在页面上展示验证码
首先后端需要先定义一个生成验证码的路由和视图。
path('img_code/', account.img_code, name='img_code'),
def img_code(request):
img, char_code = picture_code()
# 将图片数据写到内存
stream = BytesIO()
img.save(stream, format='png')
# 将验证码放到session中 也可以放到redis中
request.session['image_code'] = char_code
# 图片验证码有效期,单位秒
request.session.set_expiry(settings.IMAGE_CODE_EXPIRY)
return HttpResponse(stream.getvalue())
怎么在前端页面中展示验证码:
<img id="img_code" height="34" width="120" src="{% url "img_code" %}" alt="">
完整页面代码:
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="account">
<h2 class="text-center">用户登录</h2>
<form id="form_data">
{% csrf_token %}
{% for field in form %}
{% if field.label == '验证码' %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="row">
<div class="col-xs-6">
{{ field }}
<span class="error_msg"></span>
</div>
<div class="col-xs-6">
<img id="img_code" height="34" width="120" src="{% url "img_code" %}" alt="">
</div>
</div>
</div>
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="error_msg"></span>
</div>
{% endif %}
{% endfor %}
<div>
<button type="button" id="login" class="btn btn-primary">登 录</button>
<div class="pull-right">
<a href="{% url 'login_sms' %}">短信验证码登录>>></a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
发送登录数据和点击刷新验证码:
// 发送登录数据
function sendLoginData() {
// 获取用户输入的所有数据
// 将数据发送到后端
// 根据响应结果进行一些页面效果处理
// 绑定点击事件
$('#login').click(function () {
var data = $('#form_data').serialize(); //拿到form表单中的所有数据
$.ajax({
url: '{% url 'login' %}',
type: 'post',
data: data,
success:function (res) {
if (res.status){
location.href = res.path;
}else{
$.each(res.error_msg,function (k,v){
$('#id_' + k).next().text(v)
})
}
}
})
})
}
// 点击刷新图片验证码的事件
function refreshImageCode() {
$('#img_code').click(function () {
var oldSrc = $(this).attr('src');
$(this).attr('src',oldSrc + '?'); // 问号会重新发送一次请求
})
}
views.py:
def login(request):
if request.method == 'GET':
form = LoginForm(request)
return render(request, 'user/login.html', dict(form=form))
else:
form = LoginForm(request, data=request.POST)
if form.is_valid():
return JsonResponse(dict(status=True, path=reverse('index')))
else:
return JsonResponse(dict(status=False, error_msg=form.errors))
form校验类:
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Q
from web import models
from web.utils.bootstrap_style import BootStrapForm
from web.utils.enc import set_md5
class LoginForm(BootStrapForm, forms.Form):
'''手机号或者邮箱登录'''
username = forms.CharField(label='手机号或者邮箱')
password = forms.CharField(widget=forms.PasswordInput, label='密码')
image_code = forms.CharField(label='验证码')
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
def clean_password(self):
'''密码加密传输'''
password = self.cleaned_data.get('password')
return set_md5(password)
def clean_image_code(self):
'''验证图片验证码
* 用户输入的和后台保存的要一致
'''
user_code = self.cleaned_data.get('image_code', '')
session_code = self.request.session.get('image_code', '')
if not session_code:
raise ValidationError('验证码已经过期!!')
if user_code.strip().upper() != session_code.upper():
raise ValidationError('验证码错误!!!')
return session_code
def clean(self):
'''手机号邮箱校验'''
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
uname_or_email = models.UserInfo.objects.filter(
Q(Q(phone=username)| Q(email=username)),
password=password
).exists()
if not uname_or_email:
self.add_error('username','用户名或密码错误!!')
return self.cleaned_data