登录,生成随机图片验证码

一、登录 - 随机生成图片验证码

1、随机生成验证码

  Python随机生成图片验证码,需要使用PIL模块,安装方式如下:

  pip3 install pillow

  1)创建图片

from PIL import Image
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
with open('code.png', 'wb') as f: # 保存在本地(即写入硬盘)
img.save(f, format='png')

  参数说明:

  mode='RGB'  表示以RGB来表示颜色

  size=(120,30)  表示坐标

  color=(255, 255, 255)  表示白色

  此时,打开启动文件所在目录,里面就有了一个宽120,高30的白色code.png图片。

  2)创建画笔(用于在图片上画任意内容)

from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB') # 创建画笔对象draw
img.show() # 在图片查看器中打开,这句会调用系统默认的图片管理工具

  3)画点 - point()方法

 
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# point()第一个参数:表示坐标, 第二个参数:表示颜色
draw.point([100, 20], fill='red')
draw.point([60, 10], fill=(0, 255, 0))
# 保存在本地
with open('code.png', 'wb') as f:
img.save(f, format='png')
 

效果如下图:

  4)画线 - line()方法

 
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(540, 150), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# line()第一个参数:表示起始坐标和结束坐标,第二个参数:表示颜色
draw.line((50, 50, 50, 150), fill='red')
# 上面一句表示画一条坐标(x=100,y=100)到(x=100,y=300)的直线
draw.line((50, 100, 150, 50), fill=(120, 120, 120))
draw.line((50, 50, 150, 50), fill=(0, 255, 255))
with open('code.png', 'wb') as f:
img.save(f, format='png')
 

效果如下:

  5)画圆 - arc()方法

 
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(500, 140), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标和结束坐标(圆要画在其中间,两点确定的矩形的内切圆)
# 第二个参数:表示开始角度
# 第三个参数:表示结束角度
# 第四个参数:表示颜色
draw.arc((200, 20, 300, 120), 0, 360, fill='red')
with open('code.png', 'wb') as f:
img.save(f, format='png')
 

效果如下:

  6)写文本 - text()方法

 
from PIL import Image, ImageDraw
img = Image.new(mode='RGB', size=(80, 20), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标,第二个参数:表示写入的文本,第三个参数:表示颜色
draw.text([0, 0], 'python', 'red')
with open('code.png', 'wb') as f:
img.save(f, format='png')
 

效果如下:

  7)特殊字体文字(下载好引用的字体文件)

 
from PIL import Image, ImageDraw, ImageFont
img = Image.new(mode='RGB', size=(120, 40), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
font = ImageFont.truetype('kumo.ttf', 28)
# 第一个参数:表示字体文件路径
# 第二个参数:表示字体大小 draw.text((0, 0), 'python', 'red', font=font)
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
# 第四个参数:表示字体
with open('code.png', 'wb') as f:
img.save(f, format='png')
 

效果如下:

  8)随机生成图片验证码

 
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='../static/font/kumo.ttf', font_size=28):
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB') def rndChar():
"""
生成随机字符(包括大小写字母和数字)
:return:
"""
ranNum = str(random.randint(0, 9))
ranLower = chr(random.randint(65, 90))
ranUpper = chr(random.randint(97, 120))
return random.choice([ranNum, ranLower, ranUpper]) def rndColor():
"""
生成随机颜色
:return:
"""
return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = ( height - font_size ) / 2
draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor()) # 对图像加滤波 - 深度边缘增强滤波
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return img, ''.join(code) if __name__ == '__main__':
# 1. 直接打开,即用图片查看器查看
# img,code = check_code()
# img.show() # 2. 写入文件
# img,code = check_code()
# with open('code.png','wb') as f: # f是写入磁盘的文件句柄
# img.save(f, format='png')
# data = f.read() # data是读取图片的字节 # 3. 写入内存(Python3)
# img,code = check_code()
# from io import BytesIO # 内存管理的模块
# stream = BytesIO() # stream是写入内存的文件句柄
# img.save(stream, 'png')
# data = stream.getvalue() # 4. 写入内存(Python2)
# img,code = check_code()
# import StringIO
# stream = StringIO.StringIO() # stream是写入内存的文件句柄
# img.save(stream, 'png')
# data = stream.getvalue()
 

效果如下:

2、基于ajax实现登录的示例代码

  1)urls.py中关于登录代码:

path('login/', views.login,),  # 获取登录页面url
path('get_identifyCode/', views.get_identifyCode,), # 获取验证码对应url

  2)login.html核心代码:

 
<body>
<div id="particles-js">
<div class="login">
<p class="login-top">登录</p>
{% csrf_token %}
<div class="login-center clearfix">
<label class="" for="user">用户名</label>
<input type="text" id="user" placeholder="用户名" />
</div> <div class="login-center clearfix">
<label class="iconfont labelFS" for="pwd">密码</label>
<input type="password" id="pwd" placeholder="密码" />
</div> <div class="login-center clearfix">
<label class="iconfont labelFS" for="validcode"></label>
<input type="text" id="validcode" placeholder="验证码" />
<img src="/get_identifyCode/" alt="验证码" title="换一张" class="validImg" id="img" width="88" height="30" >
</div>
<a href="javascript:void(0);" class="login_btn">登录</a>
<p class="error"></p>
</div>
</div> <script src="jquery.min.js"></script> <script>
// ajax 登录
$(".login_btn").click(function () {
$.ajax({
url:"",
type:"post",
// data发送urlencoded格式就行,数据没那么深,没必要发json格式
data:{
user:$("#user").val(),
pwd:$("#pwd").val(),
validcode:$("#validcode").val(),
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
},
success:function (response) {
console.log(response);
if(response.user){
// 登录成功
location.href="/index/"
}
else{
// 登录失败
$(".error").html(response.err_msg)
}
}
})
}); // 验证码刷新:img标签有一个天然的发请求的模式,即src的路径后边拼接一个问号就会发一次请求,利用这一原理可以实现验证码刷新
$("#img").click(function () {
this.src += "?"
}); </script>
</body>
 

  3)views.py中获取随机验证码的视图函数代码(验证码保存利用session)

 
def get_identifyCode(request):
img,code = check_code() # 利用上面的模块得到img对象和验证码code f = BytesIO() # 得到写入内存的文件句柄
img.save(f, "png") # 写入内存
data = f.getvalue() # 从内存中读出 # 将验证码存在各自的session中,这样做的好处是每个人都有自己的验证码,不会相互混淆(一定不能设为全局变量)
request.session['keep_str'] = code return HttpResponse(data)
 

  4)views.py中login视图函数代码

 
from django.contrib import auth
def login(request):
# if request.method == "POST":
if request.is_ajax(): # 判断是否ajax请求
user = request.POST.get("user")
pwd = request.POST.get("pwd")
validcode = request.POST.get("validcode")
# Ajax请求通常返回一个自己构建的字典
response={"user": None, "err_msg": ""}
# request.session.get("keep_str")取出session中验证码与用户输入作判断
if validcode.upper() == request.session.get("keep_str").upper():
user_obj = auth.authenticate(username=user, password=pwd)
print("user_obj", user_obj, bool(user_obj))
if user_obj:
response["user"] = user
auth.login(request, user_obj) # 保存用户状态
else:
response['err_msg'] = "用户名或者密码错误!"
else:
response["err_msg"] = "验证码错误!"
return JsonResponse(response)
else:
return render(request, "login.html")
 

二、基于ajax和forms组件实现注册示例

1)model.py

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):  # 将原生auth_user表扩展一个tel手机号字段
tel=models.CharField(max_length=32)

2)forms.py(其实代码放在哪里没关系,最重要的是程序能找到,为了解耦,我们可以定义一个form.py)

 
from django import forms
# exceptions中存着django的所有错误,错误在核心组件中
from django.core.exceptions import ValidationError
from django.forms import widgets
from app01.models import UserInfo class UserForm(forms.Form): # UserForm中定义需要校验的字段
username=forms.CharField(min_length=5,
label="用户名")
password=forms.CharField(min_length=5,
widget=widgets.PasswordInput(),
label="密码")
r_pwd=forms.CharField(min_length=5,
widget=widgets.PasswordInput(),
label="确认密码")
email=forms.EmailField(min_length=5,
label="邮箱") def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs.update({'class': 'form-control'}) # 统一加class def clean_user(self):
val=self.cleaned_data.get("username")
user=UserInfo.objects.filter(username=val).first()
if user:
raise ValidationError("用户已存在!")
else:
return val def clean_pwd(self):
val=self.cleaned_data.get("password")
if val.isdigit():
raise ValidationError("密码不能是纯数字!")
else:
return val def clean_email(self):
val = self.cleaned_data.get("email")
if re.search("\w+@163.com$", val):
return val
else:
raise ValidationError("邮箱必须是163邮箱!") def clean(self):
pwd=self.cleaned_data.get("password")
r_pwd=self.cleaned_data.get("r_pwd") if pwd and r_pwd and r_pwd!=pwd:
self.add_error("r_pwd", ValidationError("两次密码不一致!"))
else:
return self.cleaned_data
 

3)reg.html核心代码:

 
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="">{{ field.label }}</label>
{{ field }}
<span class="error"></span>
</div>
{% endfor %}
<input type="button" class="btn btn-primary reg_btn" value="注册">
</form>
</div>
</div>
</div>
<script src="jquery.min.js"></script>
<script>
$(".reg_btn").click(function () {
$.ajax({
url:"",
type:"post",
data:{
username:$("#id_username").val(),
password:$("#id_password").val(),
r_pwd:$("#id_r_pwd").val(),
email:$("#id_email").val(),
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() },
success:function (res) {
if (res.user){
// 注册成功
location.href="/login/"
}
else{
// 清除错误
$(".error").html("");
// 展示新的错误
$.each(res.err_msg,function (i,j) {
$("#id_"+i).next().html(j[0]);
})
}
}
})
})
</script>
</body>
 

4)views.py中注册的视图函数reg

 
def reg(request):
if request.method == "POST":
form = UserInfo(request.POST)
res = {"user": None, "err_msg": ""}
if form.is_valid():
res["user"] = form.cleaned_data.get("username")
del form.cleaned_data["r_pwd"] # 因表中无此字段,只需校验,不插入
UserInfo.objects.create_user(**form.cleaned_data)
else:
res["err_msg"] =form.errors
return JsonResponse(res)
else: # get请求
form = UserInfoModelForm()
return render(request,"reg.html",{"form": form})
 

三、补充知识点

1、对原生auth_user表扩展字段(使用AbstractUser)

  我们之前学习用户认证组件时,用的是django提供的auth_user表,即通过引入User对象(from django.contrib.auth.models import User)去操作它,我们又发现源码中User类继承了AbstractUser类,所以AbstractUser和User其实就是一张表,所以当我们想要有用户认证功能,又想要一些auth_user表中没有的字段时,可以按照如下这样做:

  在models.py中,引入AbstractUser,并且自己定义一个用户类(表),这时类中只定义django的auth_user表中没有而你又想使用的字段即可,并且让你定义的类继承AbstractUser,如下:

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
tel=models.CharField(max_length=32) # 扩展了一个手机号字段

  注意:写完以上代码就直接去迁移数据库会报错“HINT: Add or change a related_name argument to the definition for ......”,我们需要在settings.py中加上下面这句话,来告诉Django我们要使用自己定义的表作为用户认证表(因此登录的使用方法不变,认证时django会自己去找这张表):

AUTH_USER_MODEL="app01.UserInfo"

  这时再去进行数据库迁移,我们发现,数据库中没有auth_user表了,而我们自己定义的表中除了有自己定义的那些字段外,还有之前auth_user表中的所有字段,这就代表已经达到了我们的目的。

  补充:通过命令创建超级用户的方式:

Tools -- > Run manage.py Task    # 运行起来manage.py,再输入如下命令
manage.py@myproject > createsuperuser # 执行后根据提示输入用户名,密码,邮箱
# 注意:输入的密码会进行加密处理,再存入表中,并且命令输入密码要求最少8位

2、JsonResponse的使用

我们发现,一般浏览器发送Ajax请求给服务器时,都会返回一个字典,我们需要先将字典序列化,浏览器接收到后再进行反序列化,你会不会觉得这样做有点繁琐?其实,django为我们提供了一个JsonResponse类,它为我们做好了json的序列化,并且浏览器接收到之后,ajax也会自动为我们反序列化,即ajax中success函数接收到的response就是反序列化之后的数据,直接使用即可,如上面登录示例部分代码:

 
from django.http import JsonResponse      # 引入JsonResponse
def login(request):
if request.is_ajax():
......
response={"user":None, "err_msg": ""}
......
return JsonResponse(response)
 

  分析原因:JsonResponse本质也继承了HttpResponse,而且既为我们做了序列化的操作,还将数据格式设置为json,ajax收到设置了json格式的数据也会为我们自动反序列化,也说明了不仅仅请求头中有content-type,响应头中也有,JsonResponse源码如下:

 
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)
 

3、forms组件中对渲染出来的input输入框统一增加一个类名

  我们在学习forms组件时,可以分别给每个字段设置一个类名,如class="form-control",但发现像之前那样写的有代码冗余的问题,按照如下方式写可以解决此问题:

from django import forms
from django.forms import widgets
class UserForm(forms.Form):
user=forms.CharField(min_length=5,
label="用户名")
pwd=forms.CharField(min_length=5,
widget=widgets.PasswordInput(),
label="密码")
r_pwd=forms.CharField(min_length=5,
widget=widgets.PasswordInput(),
label="确认密码")
email=forms.EmailField(min_length=5,
label="邮箱") def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for filed in self.fields.values():
filed.widget.attrs.update({'class': 'form-control'})

4、关于全局钩子__all__的问题

我们知道全局钩子的错误信息都在__all__中,源码中是这样写的:

所以知道了这些,我们可以自己设置全局钩子的字段,避免跟其他字段规律不一致造成单独判断的问题,如下方式:

                             
  # 全局钩子:校验两次密码不一致
def clean(self):
pwd=self.cleaned_data.get("pwd")
r_pwd=self.cleaned_data.get("r_pwd") if pwd and r_pwd and r_pwd!=pwd:
self.add_error("r_pwd", ValidationError("两次密码不一致!"))
# 自己定义错误信息对应的字段是r_pwd
else:
return self.cleaned_data
 

5、关于具有提交功能的按钮问题

我们知道form表单是浏览器向服务器发请求的一种方式,提交按钮也有多种,但是要注意,具有提交功能的按钮有两种:<input type="submit" value="提交" />和<button>提交</button>,也就是说,当你想用form表单发请求时,可以用以上两种的任一种,但是当你想基于ajax发送请求时,若有form标签,则一定不要用以上两种提交按钮,否则当你点击按钮发送ajax时会自动以form表单的方式再发一次请求,使用<input type="button" value="提交" />是可以的,因为它没有提交form表单功能。

原文地址:https://www.cnblogs.com/li-li/p/9911603.html

Django登录(含随机生成图片验证码)注册实例的更多相关文章

  1. Django---登录(含随机生成图片验证码)、注册示例讲解

    登录(验证码).注册功能具体代码 # urls.py from django.contrib import admin from django.urls import path from app01 ...

  2. python 全栈开发,Day85(Git补充,随机生成图片验证码)

    昨日内容回顾 第一部分:django相关 1.django请求生命周期 1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端 请求头和请求体中会包含浏览器的动作(action),这 ...

  3. servletResponse 随机生成图片验证码

    /***********************************servlet页面************************************/ package response; ...

  4. Java代码随机生成图片验证码

    package com.rchm.util.images; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2 ...

  5. python 随机生成图片验证码背景RGB-浅色或者深色

    import random def random_color(is_light = True): return (random.randint(0 ,127) + int(is_light) * 12 ...

  6. django项目登录中使用图片验证码

    应用下创建untils文件夹放置封装图片验证码的函数 创建validCode.py文件定义验证码规则 import random def get_random_color(): return (ran ...

  7. Django——13 Auth系统 登陆注册实例 权限的实现

    Django Auth系统中的表 注册登陆实例 权限的实现 登陆权限 操作权限 组操作  Auth系统中的表 从表的名称我们就能看出,auth_user,auth_group,auth_permiss ...

  8. python - django (实现电子邮箱的账户注册和验证码功能)

    使用 Django 来做一个电子邮箱注册 并 发送验证码的功能 (此处以 163 邮箱为例) 一. 登陆 163 邮箱账号,  然后进行下列操作 二. settings 配置文件 # 发送邮箱验证码 ...

  9. Django中生成随机验证码(pillow模块的使用)

    Django中生成随机验证码 1.html中a标签的设置 <img src="/get_validcode_img/" alt=""> 2.view ...

随机推荐

  1. functional-page-navigator 组件

    functional-page-navigator 组件:是一个非常强大的组件,用于跳转插件的功能页 functional-page-navigator组件的属性: version:类型 字符串 跳转 ...

  2. h5表单属性的介绍

    表单 type属性对应的属性值 text:代表文本框 案例:<input type="text" /> password:代表密码框 radio:单选框 checkbo ...

  3. MySQL主从复制 报错处理

    基于GTID的主从复制: 跳过一个事务: SET @@session.gtid_next = '冲突的GTID号';BEGIN;COMMIT; SET gtid_next = 'AUTOMATIC';

  4. [转]python常用的十进制、16进制、字符串、字节串之间的转换

    阅读目录(Content) 整数之间的进制转换: 字符串转整数: 字节串转整数: 整数转字节串: 字符串转字节串: 字节串转字符串: 测试用的python源码 进行协议解析时,总是会遇到各种各样的数据 ...

  5. URL里的分号';'一定要编码为%3b!!!!

    http://en.wikipedia.org/wiki/Query_string The series of pairs is separated by the ampersand, '&' ...

  6. 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_06 Set集合_5_HashSet存储自定义类型元素

    想存储的元素不重复,就必须重写hashCode和equals这两个方法 新建一个Person类.添加姓名和年龄这两个成员变量..get和set,有参和无参构造. 重点是重写了toString的方法 自 ...

  7. 《计算机程式设计》Week4 课堂笔记

    本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week4 Functions 4-1 System Function 函数主要分为两大类系统定义函数与使用者 ...

  8. nw打包vue项目exe更换图标

    web项目用nw打包好了之后发现没办法更换桌面显示图标问题,找了一下发现大多推荐Resource进行最后更换,试了第一次怎么也不管用,电脑重启了一下就行了...... 首先下载安装好了Resource ...

  9. android window(三)lWindow添加流程

    http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/Windo ...

  10. Winform 开源控件库( Sheng.Winform.Controls)

    升讯威 .Net WinForm 控件库提供了超过15种 Winform 控件,你可以直接使用本控件库,更可以通过本控件库学到 Winform 控件开发的方法和理念. 你可以学习到: 如何基于 Con ...