赞
踩
class Admin(models.Model):
"""管理员表"""
username = models.CharField(max_length=32, verbose_name="用户名")
password = models.CharField(max_length=64, verbose_name="密码")
admin_list.html
{% extends 'layout.html' %} {% block content %} <div style="margin-bottom: 10px"> <a type="button" class="btn btn-success" href="/admin/add"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> 添加管理员</a> <div style="width: 300px; float: right"> <form method="get" action="/admin/list"> <div class="input-group"> <input type="text" class="form-control" name="username" placeholder="Search for..." value="{{ username }}"> <span class="input-group-btn"> <button class="btn btn-default" type="submit"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button> </span> </div> </form> </div> </div> <div class="panel panel-default"> <div class="panel-heading">管理员列表</div> <div class="bs-example" data-example-id="hoverable-table"> <table class="table table-hover"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>密码</th> <th>操作</th> </tr> </thead> <tbody> {% for obj in queryset %} <tr> <td>{{ obj.id }}</td> <td>{{ obj.username }}</td> <td>******</td> <td> <a class="btn btn-success" href="/admin/{{ obj.id }}/reset_password">重置密码</a> <a class="btn btn-warning btn-sm" href="/admin/{{ obj.id }}/update">编辑</a> <a class="btn btn-danger btn-sm" href="/admin/{{ obj.id }}/del">删除</a> </td> </tr> {% endfor %} </tbody> </table> </div> <ul class="pagination"> {{ page_str }} </ul> </div> {% endblock %}
admin.py
def admin_list(request): """管理员列表""" # 搜索 data_dict = {} username = request.GET.get('username', "") if username: # {关键字: 范围, } data_dict['username__contains'] = username # data_dict = {'username__contains': 'Y'} queryset = models.Admin.objects.filter(**data_dict) # 分页功能 page_object = Pagination(request, queryset) context = { "queryset": page_object.query_set, "page_str": page_object.createHtml() } return render(request, "admin_list.html", context)
- 搜索功能
{关键字:范围} 利用字典作为搜索的条件集合
# 搜索 data_dict = {} username = request.GET.get('username', "") if username: # {关键字: 范围, } data_dict['username__contains'] = username # data_dict = {'username__contains': 'Y'} queryset = models.Admin.objects.filter(**data_dict)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 分页功能
page_object = Pagination(request, queryset) context = { "queryset": page_object.query_set, "page_str": page_object.createHtml() }
- 1
- 2
- 3
- 4
- 5
admin.py
def admin_add(request):
if request.method == "GET":
form = adminModelForm()
return render(request, "admin_add.html", {"form": form})
else:
form = adminModelForm(request.POST)
if form.is_valid():
form.save()
return redirect("/admin/list")
else:
return render(request, "admin_add.html", {"form": form})
form = adminModelForm()
在adminModelForm()
中创建表单的样式并且规定一些校验规则
class adminModelForm(BootStrapModelForm): # 新增一个确认密码字段 confirm_password = forms.CharField( max_length=64, label='确认密码', widget=forms.PasswordInput # 密码格式 # 加上后,若校验失败,密码不会清空 # widget=forms.PasswordInput(render_value=True) ) class Meta: model = models.Admin fields = ['username', 'password', 'confirm_password'] # 额外给password增加密码输入框的样式 widgets = { 'password': forms.PasswordInput # 加上后,若校验失败,密码不会清空 # widget=forms.PasswordInput(render_value=True) }
class BootStrapModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for key, value in self.fields.items():
value.widget.attrs = {'class': 'form-control', "placeholder": value.label}
def clean_username(self):
username = self.cleaned_data.get('username')
# 判断管理员是否存在
flag = models.Admin.objects.filter(username=username).exists()
if flag:
raise ValidationError("该管理员已存在")
return username
确认两次输入密码一致
encrypt.py
def md5(data_string):
# 导入django自带的sault
obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()
# 钩子函数对密码进行md5加密 def clean_password(self): password = self.cleaned_data.get('password') return md5(password) # md5加密并password返回该值 # 钩子函数校验确认密码 def clean_confirm_password(self): # txt_password 表示用户输入的md5加密完的密码 # txt_confirm_password 表示用户输入的确认密码 txt_password = self.cleaned_data.get('password') txt_confirm_password = self.cleaned_data.get('confirm_password') # md5_confirm_password 表示用户输入的md5加密后的确认密码 md5_confirm_password = md5(txt_confirm_password) if txt_password == md5_confirm_password: # 校验通过(密码一致),返回输入的数据 # 对于此案例,不保存confirm_password字段,因此用处不大 return md5_confirm_password else: raise ValidationError("密码不一致")
forms.py
class adminModelForm(BootStrapModelForm): # 新增一个确认密码字段 confirm_password = forms.CharField( max_length=64, label='确认密码', widget=forms.PasswordInput # 密码格式 # 加上后,若校验失败,密码不会清空 # widget=forms.PasswordInput(render_value=True) ) class Meta: model = models.Admin fields = ['username', 'password', 'confirm_password'] # 额外给password增加密码输入框的样式 widgets = { 'password': forms.PasswordInput # 加上后,若校验失败,密码不会清空 # widget=forms.PasswordInput(render_value=True) } def clean_username(self): username = self.cleaned_data.get('username') # 判断管理员是否存在 flag = models.Admin.objects.filter(username=username).exists() if flag: raise ValidationError("该管理员已存在") return username # 钩子函数对密码进行md5加密 def clean_password(self): password = self.cleaned_data.get('password') return md5(password) # md5加密并password返回该值 # 钩子函数校验确认密码 def clean_confirm_password(self): # txt_password 表示用户输入的md5加密完的密码 # txt_confirm_password 表示用户输入的确认密码 txt_password = self.cleaned_data.get('password') txt_confirm_password = self.cleaned_data.get('confirm_password') # md5_confirm_password 表示用户输入的md5加密后的确认密码 md5_confirm_password = md5(txt_confirm_password) if txt_password == md5_confirm_password: # 校验通过(密码一致),返回输入的数据 # 对于此案例,不保存confirm_password字段,因此用处不大 return md5_confirm_password else: raise ValidationError("密码不一致")
def admin_del(request, nid):
models.Admin.objects.filter(id=nid).delete()
return redirect("/admin/list")
views.py
def admin_update(request, nid): row_object = models.Admin.objects.filter(id=nid).first() # 先判断删除的id数据库是否存在 if not row_object: return redirect("/admin/list") title = "管理员编辑" if request.method == "GET": # instance=row_object将查询到待修改的数据填充到表单 form = admin_editModelForm(instance=row_object) return render(request, "change.html", {"form": form, "title": title}) # instance=row_object 更新后,覆盖原有数据 form = admin_editModelForm(data=request.POST, instance=row_object) if form.is_valid(): form.save() return redirect("/admin/list") return render(request, "change.html", {"form": form, "title": title})
views.py
def admin_reset_password(request, nid):
row_object = models.Admin.objects.filter(id=nid).first()
title = "重置密码--{}".format(row_object.username)
if not row_object:
return redirect("/admin/list")
if request.method == "GET":
form = admin_reset_password_ModelForm()
return render(request, "change.html", {"form": form, "title": title})
form = admin_reset_password_ModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect("/admin/list")
return render(request, "change.html", {"form": form, "title": title})
校验规则(钩子函数)
在admin_reset_password_ModelForm
中实现
def clean_password(self):
password = self.cleaned_data.get('password')
MD5_pwd = md5(password)
# form = admin_reset_password_ModelForm(data=request.POST, instance=row_object)
# 此句话为调用admin_reset_password_ModelForm返回的表单
# 利用 self.instance.pk 可以获取到row_object该行数据的id
# 判断该id对应的密码是否为输入的密码(比较的是加密完的密码)
result = models.Admin.objects.filter(id=self.instance.pk,password=MD5_pwd).exists()
if result:
raise ValidationError("不能与原密码一致!")
return MD5_pwd # md5加密并password返回该值
def clean_confirm_password(self):
# txt_password 表示用户输入的md5加密完的密码
# txt_confirm_password 表示用户输入的确认密码
txt_password = self.cleaned_data.get('password')
txt_confirm_password = self.cleaned_data.get('confirm_password')
# md5_confirm_password 表示用户输入的md5加密后的确认密码
md5_confirm_password = md5(txt_confirm_password)
if txt_password == md5_confirm_password:
# 校验通过(密码一致),返回输入的数据
# 对于此案例,不保存confirm_password字段,因此用处不大
return md5_confirm_password
else:
raise ValidationError("密码不一致")
各个部分的更新页面都差不多,因此对页面进行整合为change.html
change.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.css' %}"> <style> .navbar { border-radius: 0; } </style> {% block css %}{% endblock %} </head> <body> <nav class="navbar navbar-default"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">管理系统</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li><a href="/admin/list">管理员管理</a></li> <li><a href="/depart/list">部门管理</a></li> <li><a href="/user/list">员工管理</a></li> <li><a href="/number/list">靓号管理</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">登录</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">admin<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">个人中心</a></li> <li><a href="#">设置</a></li> <li role="separator" class="divider"></li> <li><a href="#">注销</a></li> </ul> </li> </ul> </div> </div> </nav> <div class="container"> <div class="panel panel-default"> <div class="panel-heading">{{ title }}</div> <div class="panel-body"> <div class="bs-example" data-example-id="simple-form-inline"> <form class="form" method="post" novalidate> {% csrf_token %} {#创建表单,form为userinfo各字段的表单#} {#每一个field都是一个字段的输入框#} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red">{{ field.errors.0 }}</span> {# field.errors.0显示第一条错误即可 #} </div> {% endfor %} <input type="submit" class="btn btn-success" value="提交"> </form> </div> </div> </div> </div> <script src="{% static 'js/jquery-3.7.1.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script> <script src="{% static 'plugins/bootstrap-datepicker/js/bootstrap-datepicker.js' %}"></script> <script src="{% static 'plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script> <script> $(function () { $('#dt_creat_time').datepicker({ format: 'yyyy-mm-dd', //startDate: '0',//最早日期为当前日期,无法wangqian language: "zh-CN", autoclose: true }); }) </script> {% block js %}{% endblock %} </body> </html> {% block content %} {% endblock %}
固定参数
- 表单标题
<div class="panel-heading">{{ title }}</div>
- 1
从每个操作的视图函数中传进来
- 表单
form为view函数创建好的表单标签(包含input输入框、样式、value值)<div class="panel-body"> <div class="bs-example" data-example-id="simple-form-inline"> <form class="form" method="post" novalidate> {% csrf_token %} {#创建表单,form为userinfo各字段的表单#} {#每一个field都是一个字段的输入框#} {% for field in form %} <div class="form-group"> <label>{{ field.label }}</label> {{ field }} <span style="color: red">{{ field.errors.0 }}</span> {# field.errors.0显示第一条错误即可 #} </div> {% endfor %} <input type="submit" class="btn btn-success" value="提交"> </form> </div> </div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
form标签中的action
对于form中的action关键字,指定了表单传数据的目的地,若不写默认form变量的来源位置
使用默认位置,不仅简便,而且便于整合,不用额外在action中拼接nid
login.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.css' %}"> <style> .login { position: fixed; width: 400px; height: 300px; border: 1px solid #adadad; left: 0; right: 0; top: 0; bottom: 0; margin: auto; padding: 20px 10px; box-shadow: 2px 2px 5px #8c8c8c; } .font { text-align: center; } </style> </head> <body> <div class="login"> <form method="post"> {% csrf_token %} <div class="font"><h1>用户登录</h1></div> <div class="form-group"> {% for obj in form %} <label>{{ obj.label }}</label> {{ obj }} <span style="color: red">{{ obj.errors.0 }}</span> {% endfor %} </div> <button type="submit" class="btn btn-success">登录</button> </form> </div> </body> </html>
login.py
def login(request): if request.method == 'GET': form = login_ModelForm() return render(request, 'login.html', {'form': form}) form = login_ModelForm(request.POST) # 将输入的用户名密码与数据库的用户名密码进行比对 if form.is_valid(): dic = form.cleaned_data # 判断用户名密码是否正确(是否查到) admin_object = models.Admin.objects.filter(**dic).first() if not admin_object: # 在form中添加错误(错误地方,错误) ' # 将错误信息展示到密码上 form.add_error("password", "用户名或密码错误") return render(request, 'login.html', {'form': form}) # 验证成功——开始cookie验证 # request.session['info'] = admin_object.username # 以下这句话,完成了以下功能 """ 1.生成cookie存储到浏览器中 2.将cookie和浏览器的信息存储到session中(django存到数据库中) session主要存储 key(cookie) data(根据下面数据形成的) 日期等等 """ request.session['info'] = {'id': admin_object.id, 'username': admin_object.username} return redirect('/admin/list') return render(request, 'login.html', {'form': form})
login_ModelForm()
表单类
BootStrapModelForm
对表单的样式进行更改
实现了表单的建立和对输入密码进入md5加密,便于与数据库比较class login_ModelForm(BootStrapModelForm): class Meta: model = models.Admin fields = ['username', 'password'] # 将输入的密码进行md5,加密 def clean_password(self): password = self.cleaned_data.get('password') MD5_pwd = md5(password) return MD5_pwd
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
获取表单数据的方法
form.cleaned_data.get(xxx)
该方法能获取对应name的数据,返回一个字符串
username = form.cleaned_data.get('username') password = form.cleaned_data.get('password') res = models.Admin.objects.filter(username=username,password=password).first()
- 1
- 2
- 3
form.cleaned_data
该方法返回表单中所有的输入数据,将name与数据以字典的形式对应返回
# 返回值的字典形式可以直接传入查询数据库的条件 dic = form.cleaned_data # 判断用户名密码是否正确(是否查到) admin_object = models.Admin.objects.filter(**dic).first()
- 1
- 2
- 3
- 4
错误信息的提示
对于用户名与密码不匹配的情况,要显示错误提示并重新登录
- 判断过程中需要获取输入的数据以及识别失败返回登陆页面,因此需要在视图函数中判断,而不能在钩子函数实现(不匹配时需返回登陆页面)
- 判断表单是否返回数据(表单是否为空)
admin_object = models.Admin.objects.filter(**dic).first() if not admin_object: form.add_error("password", "用户名或密码错误") return render(request, 'login.html', {'form': form}) request.session['info']={'id':admin_object.id,'username': admin_object.username}
- 1
- 2
- 3
- 4
- 5
form.add_error("password", "用户名或密码错误")
参数1表示错误信息显示的位置 参数2表示显示的错误信息 该语句新增错误信息到form的errors中,便于提示错误信息使用位置
login.html
<div class="login"> <form method="post"> {% csrf_token %} <div class="font"><h1>用户登录</h1></div> <div class="form-group"> {% for obj in form %} <label>{{ obj.label }}</label> {{ obj }} <span style="color: red">{{ obj.errors.0 }}</span> {% endfor %} </div> <button type="submit" class="btn btn-success">登录</button> </form> </div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
http://127.0.0.1:8000/admin/list/
https://127.0.0.1:8000/admin/list/
对于http来说,建立无状态短连接(浏览器发送请求,后端网页接收请求,并响应请求,完成这个过程后断开连接)
实现浏览器与后端网页长久通信
当浏览器向后端网页发送请求,后端网页向浏览器发送一个响应的过程中,后端网页给该浏览器生成一个为一个标识码**(字典的形式)来代表该浏览器,该标识码存储在该浏览器内,这个标识码就叫做cookie**;在后端网页给浏览器生成发送标识码时,也将该标识码与对应的浏览器存储在后端网页的session中;在之后的连接中,浏览器直接向后端网页发送cookie标识,在后端网页的session中查询是否存在,从而确定是否连接。
- cookie:随机字符串(用来标识)
- session:后端网页存储用户信息的部分,主要形式是数据库、redis、文件(django主要是数据库形式)
def login(request):
if request.method == 'GET':
form = login_ModelForm()
return render(request, 'login.html', {'form': form})
form = login_ModelForm(request.POST)
if form.is_valid():件
dic = form.cleaned_data
# 判断用户名密码是否正确(是否查到)
admin_object = models.Admin.objects.filter(**dic).first()
if not admin_object:
form.add_error("password", "用户名或密码错误")
return render(request, 'login.html', {'form': form})
request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
return redirect('/admin/list')
return render(request, 'login.html', {'form': form})
当用户名与密码匹配成功后,实现浏览器与后端网页长久通信(验证cookie)
request.session['info']={'id': admin_object.id, 'username': admin_object.username}
以下这句话,完成了以下功能:
将后端网页生成cookie存储到浏览器中
将cookie和浏览器的信息存储到session中(django存到数据库中)、
session主要存储 key(cookie) data(根据下面数据形成的) 日期等等
生成的cookie存储到session的cookie部分 信息info 存储对session对应的data部分
- 1
- 2
# 验证成功,生成cookie和session,从session获取
# request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
info_dic = request.session.get('info')
# 若未登录,则info_dic为空 若登录,则info_dic为传入的数据
if not info_dic:
return redirect('/login/')
在每一个视图函数中都加入该语句,直接视图函数时,判断是否有cookie(是否登录)
- 若已登录,则对应浏览器生成cookie并且存取相应的数据信息到session中,根据获取某个浏览器在session中的数据来判断是否处于登录状态
- 若未登录,直接跳转到登录页,不再继续执行视图函数
缺点
每一个视图函数均需要增添这些语句,非常冗余
书写中间件(类的位置):
app01-middleware-auth.py
引用的Django类:
from django.utils.deprecation import MiddlewareMixin
中间件的注册:
setting.py 中的MIDDLEWARE添加'app01.middleware.auth.M1'
浏览器向django发送请求时,需要依次经过多个中间件后,视图函数才可以接收请求.请求顺序:浏览器-M1-M2-M3-视图函数
视图函数响应请求时,也是依次经过中间件后,浏览器才可以接收到响应。响应顺序:视图函数-M3-M2-M1-浏览器
auth.py
class M1(MiddlewareMixin): def process_request(self, request): print("m1-process_request") return # return HttpResponse("无权访问") def process_response(self, request, response): print("m1-process_response") return response class M2(MiddlewareMixin): def process_request(self, request): print("m2-process_request") return def process_response(self, request, response): print("m2-process_response") return response class M3(MiddlewareMixin): def process_request(self, request): print("m3-process_request") return def process_response(self, request, response): print("m3-process_response") return response
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
请求与响应顺序:
m1-process_request m2-process_request m3-process_request m3-process_response m2-process_response m1-process_response
- 1
- 2
- 3
- 4
- 5
- 6
若在依次请求中间件时,某个中间件直接返回响应,则其他中间件不在执行,直接响应浏览器
用于实现登录校验,符合无返回值,不符合有返回值
class M1(MiddlewareMixin): def process_request(self, request): print("m1-process_request") return HttpResponse("无权访问") def process_response(self, request, response): print("m1-process_response") return response class M2(MiddlewareMixin): def process_request(self, request): print("m2-process_request") return def process_response(self, request, response): print("m2-process_response") return response class M3(MiddlewareMixin): def process_request(self, request): print("m3-process_request") return def process_response(self, request, response): print("m3-process_response") return response
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
m1-process_request m1-process_response 中间件M1直接return HttpResponse("无权访问") 则不再执行其他中间,直接响应浏览器
- 1
- 2
- 3
- 4
模板:
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
...
...
...
return
auth.py
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
# 1.中间件只能检验出登陆视图函数的其他函数
# request.path_info 获取当前用户请求的url 判断是否为登录视图的url
# 如果是,不进行任何检验,直接进入登陆页面
print(request.path_info)
if request.path_info == '/login/':
info_dic = request.session.get('info')
if info_dic:
return
return redirect('/login')
- 中间件只能检验出登陆视图函数的其他函数
- request.path_info 获取当前用户请求的url 判断是否为登录视图的url
如果是登陆页面,不进行任何检验,直接进入登陆页面- 如果不是登录页面,则需要判断是否有登录信息
- 获取生成cookie时存的数据
- 获取生成cookie时存的数据
- 校验失败,直接返回,不允许进入视图函数
删除存储的session信息,并返回登录页面即可
login.py
def logout(request):
# 注销就是清除clear
request.session.clear()
return redirect('/login')
layout.html
... ... ... <body> <nav class="navbar navbar-default"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li><a href="/admin/list">管理员管理</a></li> <li><a href="/depart/list">部门管理</a></li> <li><a href="/user/list">员工管理</a></li> <li><a href="/number/list">靓号管理</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.session.info.username }}<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">个人中心</a></li> <li><a href="#">设置</a></li> <li role="separator" class="divider"></li> <li><a href="/logout">注销</a></li> </ul> </li> </ul> </div> </div> </nav> ... ... ...
{{ request.session.info.username }}
用户的登录信息存储在session中来源:login视图函数中
request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='Monaco.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(): # ASCII码对应字母 return chr(random.randint(65, 90)) # 生成随机颜色 def rndColor(): 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(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__': # check_code()返回一张图片以及生成的随机字母 img, code_str = check_code() print(code_str) # 保存图片 with open('code.png', 'wb') as f: img.save(f, format='png')
class login_ModelForm(BootStrapModelForm):
password = forms.CharField(widget=forms.PasswordInput, label='密码')
# 新增
code = forms.CharField(widget=forms.TextInput, label='验证码', )
class Meta:
model = models.Admin
# 新增
fields = ['username', 'password', 'code']
# 将输入的密码进行md5,加密
def clean_password(self):
password = self.cleaned_data.get('password')
MD5_pwd = md5(password)
return MD5_pwd
login.html
... ... ... <div class="form-group"> <label>{{ form.code.label }}</label> <div class="row"> <div class="col-xs-7"> {{ form.code }} <span style="color: red;font-size: 10px">{{ form.code.errors.0 }}</span> </div> <div class="col-xs-5"> {#图片的地址直接转到生成图片的视图函数中#} {#特别注意:cookie验证时不仅要排除登录视图函数,还得排除生成图片视图函数#} <img id="img_code" src="/image/code" style="width: 125px"> </div> </div> </div> ... ... ...
图片验证码的增加
app-utils-code.py
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='Monaco.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 chr(random.randint(65, 90)) # 生成随机颜色 def rndColor(): 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(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)
check_code()
返回两个值img:
<class 'PIL.Image.Image'>
类型的图片code:图片验证码上的字符串
<div class="col-xs-5">
<img id="img_code" src="/image/code" style="width: 125px">
</div>
图片的地址采用视图函数来实现,image_code视图函数自动接收生成的图片,加载内存中传入html中
def image_code(request):
img, code_string = check_code()
request.session['image_code'] = code_string
# 给session设置60s超时,超过60s,自动无效
request.session.set_expiry(60)
# 向内存传入图片
stream = BytesIO()
img.save(stream, 'png')
return HttpResponse(stream.getvalue())
- 要点1
img, code_string = check_code()
调用pillow函数生成图片img:
<class 'PIL.Image.Image'>
类型的图片code_string为图片上的文字,用于校验
- 要点2
request.session['image_code'] = code_string
验证码的校验利用session 将图片码写入自己的session中,以便于后续获取验证码进行校验
在session存储的数据也加一条 {image_code:code_string}
- 要点3
request.session.set_expiry(60)
给session设置60s超时,超过60s,自动无效
- 要点4
stream = BytesIO() img.save(stream, 'png') return HttpResponse(stream.getvalue())
- 1
- 2
- 3
向内存中传输图片,传入html中显示
图片的地址直接转到生成图片的视图函数中,特别注意:生成图片的视图函数也不需要cookie验证,cookie验证时不仅要排除登录视图函数,还得排除生成图片视图函数
auth.py
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request)
if request.path_info in ['/login/','/image/code']:
return
# 2.获取生成cookie时存的数据
info_dic = request.session.get('info')
# 校验成功,允许进入视图函数
if info_dic:
return
# 校验失败,直接返回,不允许进入视图函数
return redirect('/login')
已知在生成图片验证码时已经将图片的中的随机验证码字符串传入
request.session['image_code'] = code_string
,因此request.session中多存储一条code_string的信息。 如此,用户输入完验证码时,通过
form.cleaned_data
获取前端表单输入的数据,取出图片验证码code与session中的code_string进行对比即可
def login(request): if request.method == 'GET': form = login_ModelForm() return render(request, 'login.html', {'form': form}) form = login_ModelForm(request.POST) if form.is_valid(): # 用户输入的code 并且取出form.cleaned_data中的code,此时form.cleaned_data仅有username和password信息,使用户名和密码可以校验 in_code= form.cleaned_data.pop("code") # 从session提取图片字母 image_code = request.session.get('image_code','') # 判断验证码 if not (in_code.upper()==image_code): # 在form中添加错误(错误地方,错误) ' # 将错误信息展示到密码上 form.add_error("code", "验证码错误") return render(request, 'login.html', {'form': form}) # 判断用户名密码是否正确(是否查到) admin_object = models.Admin.objects.filter(**form.cleaned_data).first() if not admin_object: form.add_error("password", "用户名或密码错误") return render(request, 'login.html', {'form': form}) request.session['info'] = {'id': admin_object.id, 'username': admin_object.username} # cookie 保存7天 request.session.set_expiry(60*60*60*7) return redirect('/admin/list') return render(request, 'login.html', {'form': form})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。