此文专门记录一些用到的新知识。
一.Form组件验证登录:
#Form表单定制验证码:
code = fields.CharField(widget=widgets.TextInput(
attrs={'class': "form-control", 'placeholder': '验证码'}), )
#将request一并传入,用来获取session,因此在使用Form组件时,实例化时要传request
#如 obj = RegForm(request,request.POST)
def __init__(self,request,*args,**kwargs):
super().__init__(*args,**kwargs)
self.request = request
#自定义验证规则,验证用户输入的验证码是否正确
def clean_code(self):
#取到已经写入session的验证码'code'
session_code = self.request.session.get('code')
#与取到的用户输入值进行匹配,判断是否正确
input_code = self.cleaned_data.get('code')
if session_code == input_code:
return input_code
raise ValidationError('验证码错误')
#验证码视图函数
from utils.random_check_code import rd_check_code
from io import BytesIO
from utils.FormInfo import LoginInfo,RegForm
def check_code(request):
#使用自定制的验证码生成函数,得到返回值为 img:验证码生成的图片,code:验证码生成的实际字符串
img, code = rd_check_code()
#打开内存写入验证码图片,BytesIO可以写入二进制文件
stream = BytesIO()
img.save(stream, 'png')
#将生成的验证码字符串写入session便于Form组件的验证
request.session['code'] = code
#getvalue()方法可以获取写入的数据,即此函数发送的是生成的图片
return HttpResponse(stream.getvalue())
#实际应用:注册用户为例
#后端代码:
def reg(request):
if request.method == 'GET':
#即使GET时无需验证,但Form规定了要传一个request,所以这里也要传参
obj = RegForm(request)
return render(request,'sign.html',{'obj':obj})
else:
#传入request便于获取session验证
obj = RegForm(request,request.POST)
if not obj.is_valid():
return render(request,'sign.html',{'obj':obj})
else:
obj.cleaned_data.pop('pwd_again')
models.UserInfo.objects.create(**obj.cleaned_data)
return redirect('/login.html')
前端代码:
<div class="form-group" style="margin-top: 60px">
<label class="col-sm-2 control-label">验证码</label>
<div class="col-sm-3">
{{ obj.code }}
</div>
<div class="col-sm-5">
<!-- 验证码图片写上/check_code/,Django urls里再配置好指向处理验证码的函数,
此时页面加载向check_code发请求,返回的就是生成好的图片达到生成验证码的效果 -->
<img style="width: 120px;height: 30px;" src="/check_code/"
id="img-code">
</div>
<span style="color: red"> {{ obj.errors.code.0 }}</span>
</div>
验证码生成函数代码:
#需要安装PIL模块生成图片
from PIL import Image,ImageDraw,ImageFont,ImageFilter
import random
#font_file指向字体文件路径:
def rd_check_code(width=120, height=30, char_length=5, font_file='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:
"""
return chr(random.randint(65, 90))
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 = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
# 写干扰点
for i in range(10):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
# 写干扰圆圈
for i in range(10):
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(3):
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)
clean()方法是其它全部的验证规则执行完毕才会执行,通常可以用来对多个字段之间进行共同验证,比如验证2次输入密码是否一致。
def clean(self):
p1 = self.cleaned_data.get('password')
p2 = self.cleaned_data.get('password2')
if p1 == p2:
#clean方法return的不是字段
return None
#同样的,添加错误是往None添加,在试图函数中通过obj.errors['__all__']取值,
而在模版中则通过obj.errors.non_field_errors取值,此为常规写法。
# self.add_error(None,ValidationError('密码不一致'))
#当然,使用add_error函数可以往None里添加,自然也能往我们的字段中添加,
即key,value中key改为我们的字段名,即模版中相对应也可以obj.errors.字段名来取。
self.add_error("password2",ValidationError('密码不一致'))
二.models应用:
1.分组:
#分类函数
from django.db.models import Count,Min,Max,Sum
#values里写以什么字段进行分组以及可以取到的字段,然后.annotate(别名=Count('聚合计算个数的字段,也可以直接写1'))
cate_list = models.Article.objects.filter(blog__site=site).values('category__title','category__nid').annotate(c=Count('nid'))
tags_list = models.Article.objects.filter(blog__site=site).values('tags__title','tags__nid').annotate(c=Count(1))
#时间查询需要涉及到时间的格式化,额外的东西所以这里用到extar
#mysql查询
# data_time = models.Article.objects.extra(select={'c':'date_fomat(create_time,"%%Y-%%m")'}).values('c').annotate(ct=Count('nid'))
#sqlite3查询
date_list = models.Article.objects.filter(blog__site=site).extra(select={'c':'strftime("%%Y-%%m",create_time)'}).\
values('c').annotate(ct=Count('nid'))