Django之cookie分页与装饰器(九)
一、不用cookie方式的分页
将分页功能封装一个类:
在主目录下面创建一个目录并创建一个py文件:
pagination.py:
from django.utils.safestring import mark_safe class Page: def __init__(self,current_page,data_count,per_page_count=8,page_num = 9): ''' :param current_page: 当前页 :param data_count: 数据的总数目 :param per_page_count: 每页显示的数目 :param page_num: 显示几页内容 ''' self.current_page = current_page self.data_count = data_count self.per_page_count=per_page_count self.page_num = page_num @property def start(self): ''' :return: 返回得到起始 ''' return (self.current_page-1)*self.per_page_count @property def end(self): ''' :return: 返回结束 ''' return self.current_page*self.per_page_count @property def total_count(self): ''' :return: 返回总页数 ''' v, y = divmod(self.data_count, self.per_page_count) if y: v += 1 return v def page_str(self,base_url): ''' :param base_url: 这里是用于自定义url前缀 :return: 返回的为页面下端要显示的跳转页的html语言的字符串 ''' page_list = [] if self.total_count < self.page_num: start_index = 1 end_index = self.total_count + 1 else: if self.current_page <= (self.page_num + 1) / 2: start_index = 1 end_index = self.page_num + 1 else: start_index = self.current_page - (self.page_num - 1) / 2 end_index = self.current_page + (self.page_num + 1) / 2 if self.current_page + (self.page_num + 1) / 2 > self.total_count: end_index = self.total_count + 1 start_index = self.total_count - self.page_num + 1 if self.current_page == 1: prev = '<a class="page" href="#">上一页</a>' else: prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url,self.current_page - 1) page_list.append(prev) for i in range(int(start_index), int(end_index)): if i == self.current_page: temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url,i, i) else: temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url,i, i) page_list.append(temp) if self.current_page == self.total_count: nex = '<a class="page" href="#">下一页</a>' else: nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url,self.current_page + 1) page_list.append(nex) go_page = """ <input type='text' /><a onclick="jumpTo(this,'%s?p=');">跳转</a> <script> function jumpTo(ths,base){ var val = ths.previousSibling.value; location.href = base + val; } </script> """ %(base_url) page_list.append(go_page) page_str = "".join(page_list) page_str = mark_safe(page_str) return page_str
urls.py路由里面添加一条规则:
url(r'^user_list/', views.user_list),
views.py文件设置:
from utils import pagination LIST = [] for i in range(500): LIST.append(i) def user_list(request): current_page = request.GET.get("p",1) current_page = int(current_page) page_obj = pagination.Page(current_page,len(LIST)) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str("/user_list/") return render(request,"user_list.html",{"list":data,"page_str":page_str})
user_list.html文件设置:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .pagination .page{ display: inline-block; padding: 5px; background-color: cyan; margin: 5px; } .pagination .page.active{ background-color: brown; color: white; } </style> </head> <body> <ul> {% for item in list%} {% include 'li.html' %} {% endfor %} </ul> <div class="pagination"> {{ page_str }} </div> </body> </html>
li.html文件设置:
<li>{{ item }}</li>
下面是测试截图:
注意:因为在前端防止防止xss攻击,要实现前后端字符串的传递,可以通过前端{{ page_str|save}},后端:可以通过导入from django.utils.safestring import mark_safe,然后page_str = mark_safe(page_str)。
二、利用cookie分页
客户端浏览器上的一个文件以字典的方式存在,可以让用户登录一次之后多少时间之内免重复登录。
2.1 先来一个cookie登录的小例子:
配置:
urls.py:
url(r'^login/', views.login), url(r'^index/', views.index),
views.py:
from django.shortcuts import render, HttpResponse,redirect def login(request): print(request.method) if request.method=="GET": return render(request,"login.html") if request.method =="POST": u = request.POST.get("username") p = request.POST.get("pwd") dic = user_info.get(u) print(u,p) if not dic: return render(request,"login.html") if dic["pwd"] == p: res = redirect("/index") res.set_cookie('username1',u) return res else: return render(request, "login.html") def index(request): #获取当前登录的用户名 v = request.COOKIES.get("username1") if not v: return redirect("/login") return render(request,"index.html",{"current_user":v})
index.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>欢迎登录:{{ current_user }}</h1> </body> </html>
login.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="/login/" method="POST"> <input type="text" name="username" placeholder="用户名" /> <input type="password" name="pwd" placeholder="密码" /> <input type="submit" /> </form> </body> </html>
测试一下:
http://127.0.0.1:8000/tpl1/ #你可以先访问其他的域名一下会发现不收是否登录限制,因为它们没有做cookie的判断。
下面看下cookie是怎么传递的:
#让后服务端就是根据浏览器发过来的cookies做判断是否需要用户登录。
设置cookie:
res.set_cookie(key,value)
参数:
key #键 value= '' #值 max_age=None #超时时间,以秒作为单位。默认是关闭浏览器失效 expires=None #超时时间,这个是可以设置datatime path="/" #Cookie生效的路径 domain=None #Cookie生效的域名 secure=False #https传输 httponly=False #只能http协议传输,无法被JavaScript获取
小记录:
# 设置cookie,关闭浏览器失效 response.set_cookie('key',"value") # 设置cookie, N秒后失效 response.set_cookie('username1',"value",max_age=10) # 设置cookie, 截止时间失效 import datetime current_date = datetime.datetime.utcnow() #打印结果类似于2018-02-22 06:51:00.612760 current_date = current_date + datetime.timedelta(seconds=5) #这是当前时间加上5秒。datetime.timedelta(seconds=5)打印出来是0:00:05 response.set_cookie('username1',"value",expires=current_date) #这是就是设置cookie截止到什么时间点失效 #response.set_cookie('username1',"value",max_age=10)
2.2 结合cookie来做分页
设置:
views.py:
from utils import pagination LIST = [] for i in range(500): LIST.append(i) def user_list(request): current_page = request.GET.get('p', 1) current_page = int(current_page) val = request.COOKIES.get('per_page_count',10) print(val,type(val)) val = int(val) print(val,type(val)) page_obj = pagination.Page(current_page,len(LIST),val) data = LIST[page_obj.start:page_obj.end] page_str = page_obj.page_str("/user_list/") return render(request, 'user_list.html', {'li': data,'page_str': page_str})
user_list.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .pagination .page{ display: inline-block; padding: 5px; background-color: cyan; margin: 5px; } .pagination .page.active{ background-color: brown; color: white; } </style> </head> <body> <ul> {% for item in li %} {% include 'li.html' %} {% endfor %} </ul> <div> <select id="ps" onchange="changePageSize(this)"> <option value="10">10</option> <option value="20">20</option> <option value="30">30</option> <option value="40">40</option> </select> </div> <div class="pagination"> {{ page_str }} </div> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function () { var v = $.cookie("per_page_count"); //$.cookie("per_page_count")是读取Cookie "per_page_count"的值 $("#ps").val(v); //然后找到id是ps的元素的val并将其值覆盖为v也就是当前页面应该显示多少行内容 }); function changePageSize(ths) { var v = $(ths).val(); console.log(v); $.cookie("per_page_count",v); location.reload() } </script> </body> </html>
测试:
#首先一开始肯定是服务端传送给浏览器一个Cookie:per_page_count=10,然后通过select标签我们可以将值改为10-40,然后就会触发changePageSize函数,它就会将当前select的val值覆盖本地浏览器Cookie:per_page_count的值,然后location.reload()就是重新发起了get请求,然后服务器views视图函数哪里就会get客户端发送过来的Cookie的per_page_count的值,然后这个值就是客户端提交过来的,然后处理完毕之后,浏览器就显示想要的效果了。
#上图是通过views视图函数那里定义的print,将每次更改后的Cookie中的per_page_count的值打印了一下。
2.3 关于cookie的加密
前面通过:res.set_cookie('username1',u) 设置cookie。
还有一种加密的方式,即:res.set_signed_cookie("username1",u,salt="jiami")
通过salt这个参数实现加密,同样的获取cookie的时候也需要加上salt参数才能进行解密。
三、用户认证装饰器
3.1 FBV的装饰器用法
#上面简单做了个cookie登录验证的小例子,但是有问题,就是每个views函数想实现验证登录都要写好多判断,所以这里就需要装饰器了。
views.py:
def auth(func): def inner(reqeust,*args,**kwargs): v = reqeust.COOKIES.get('username1') if not v: return redirect('/login/') return func(reqeust, *args,**kwargs) return inner @auth def index(reqeust): # 获取当前已经登录的用户 v = reqeust.COOKIES.get('username1') return render(reqeust,'index.html',{'current_user': v}) @auth def tpl1(request): user_list = [1, 2, 3, 43] return render(request, 'tpl1.html', {'u': user_list}) def tpl2(request): user_list = [11, 22, 33, 343] return render(request, 'tpl2.html', {'u': user_list})
下面测试一下:
#可以看到tpl1加了装饰器就验证此用户是否登录了,tpl2没有加装饰器就没有验证。
3.2 CBV的装饰器用法
urls.py:
url(r'^order/', views.Order.as_view()),
先来一个普通的CBV用法例子:
from django import views class Order(views.View): def get(self,request): v = request.COOKIES.get("username1") if not v: return redirect("/login") return render(request, "index.html", {"current_user": v}) def post(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v})
只对get请求做认证:
def auth(func): def inner(request,*args,**kwargs): v = request.COOKIES.get("username1") if not v: return redirect("/login") return func(request,*args,**kwargs) return inner from django import views from django.utils.decorators import method_decorator class Order(views.View): @method_decorator(auth) def get(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v}) def post(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v})
这样当访问order的时候就加上了验证功能,但是这样是只给get方法加验证,如果想要给更多得方法加验证的时候,通过下面方法实现:
def auth(func): def inner(request,*args,**kwargs): v = request.COOKIES.get("username1") if not v: return redirect("/login") return func(request,*args,**kwargs) return inner from django import views from django.utils.decorators import method_decorator class Order(views.View): @method_decorator(auth) def dispatch(self, request, *args, **kwargs): return super(Order,self).dispatch(request, *args, **kwargs) def get(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v}) def post(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v})
因为CBV每次需要先执行一个dispatch方法,在dispatch方法上加认证,这样就相当于在所有的函数上面加上了认证,但是这种方法有人会觉得多写了一个dispatch方法,可以将其简化为:
@method_decorator(auth,name="dispatch") class Order(views.View): def get(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v}) def post(self,request): v = request.COOKIES.get("username1") return render(request, "index.html", {"current_user": v})