柴少的博客 也许终将一事无成,却不能甘于平庸。

Django之cookie分页与装饰器(九)

一、不用cookie方式的分页

将分页功能封装一个类:

在主目录下面创建一个目录并创建一个py文件:

图片.png

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>

下面是测试截图:

图片.png

注意:因为在前端防止防止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的判断。

图片.png

图片.png

下面看下cookie是怎么传递的:

图片.png

图片.png

#让后服务端就是根据浏览器发过来的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>

测试:

图片.png

图片.png

#首先一开始肯定是服务端传送给浏览器一个Cookie:per_page_count=10,然后通过select标签我们可以将值改为10-40,然后就会触发changePageSize函数,它就会将当前select的val值覆盖本地浏览器Cookie:per_page_count的值,然后location.reload()就是重新发起了get请求,然后服务器views视图函数哪里就会get客户端发送过来的Cookie的per_page_count的值,然后这个值就是客户端提交过来的,然后处理完毕之后,浏览器就显示想要的效果了。

图片.png

#上图是通过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})

下面测试一下:

图片.png

图片.png

#可以看到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})


作者:chaishaopeng 分类:Django学习 浏览:724 评论:0
留言列表
发表评论
来宾的头像