Django分页功能和中间件

分页

 自定义分页

def booklist(request):
# 查看所有书籍信息
    books = models.Book.objects.all()
    # 书籍的数量
    books_count = books.count()
    # 获取url中page 参数
    current_page = request.GET.get("page")
    try:
        current_page = int(current_page)
    except Exception as e:
        current_page = 1

    # 允许每页显示多少条数据,此处为10条
    per_page = 10
    # 页码最多显示多少个页码
    max_show = 9
    # 最多显示页码数一半, / 结果显示浮点数 , // 结果显示整数
    half_show = max_show // 2
    # 计算当前数据需要显示多少页
    page_count,more = divmod(books_count,per_page)
    if more:
        page_count += 1
    if current_page > page_count:
        current_page = page_count
    # 计算显示页码的起点和终点
    show_page_start = current_page - half_show
    show_page_end = current_page + half_show

    # 特殊判断,当前页码 - half_show <= 0 时
    if current_page - half_show <= 0:
        show_page_start = 1
        show_page_end = max_show
    # 当前页码 + half_show >= page_count
    if current_page + half_show > page_count:
        show_page_start = page_count - max_show+1
        show_page_end = page_count
    # 总共需要的页码数 < max_show
    if page_count < max_show:
        show_page_start = 1
        show_page_end = page_count

    # 每页数据的起始和终止点
    page_start = (current_page - 1) * per_page
    page_end = current_page * per_page
    # 显示每页显示的数据
    data = books[page_start:page_end]

    '''
    <nav aria-label="Page navigation">
        <ul class="pagination">
            # <li>
            #     <a href="#" aria-label="Previous">
            #         <span aria-hidden="true">&laquo;</span>
            #     </a>
            # </li>
            <li><a href="#">1</a></li>
            # <li>
            #     <a href="#" aria-label="Next">
            #         <span aria-hidden="true">&raquo;</span>
            #     </a>
            # </li>
        </ul>
    </nav>
    '''
    tmp = []
    page_html_start = '<nav aria-label="Page navigation" class="text-center"><ul class="pagination">'
    page_html_end = '</ul></nav>'
    tmp.append(page_html_start)
    # 添加一个首页
    tmp.append('<li><a href="/bk_list/?page=1">首页</a></li>')
    # 添加一个上一页
    # 当 当前页是第一页,不能点击上一页按钮
    if current_page == 1:
        tmp.append('<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
    else:
        tmp.append('<li><a href="/bk_list/?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(current_page - 1))

    # for循环添加要展示的页码
    for i in range(show_page_start,show_page_end+1):
        # 如果for循环的当前页码等于当前页码,给L标签添加active
        if current_page == i:
            tmp.append('<li class="active"><a href="/bk_list/?page={0}">{0}</a></li>'.format(i))
        else:
            tmp.append('<li><a href="/bk_list/?page={0}">{0}</a></li>'.format(i))
    #添加一个下一页
    # 当 当前页是最后一页,不能点击下一页按钮
    if current_page == page_count:
        tmp.append('<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>')
    else:
        tmp.append('<li><a href="/bk_list/?page={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(current_page + 1))
    # 添加一个尾页
    tmp.append('<li><a href="/bk_list/?page={}">尾页</a></li>'.format(page_count))
    tmp.append(page_html_end)
    page_html = ''.join(tmp)
稳扎稳打版
class MyPage(object):

    def __init__(self,current_page,books_count, url_prefix, per_page=10, max_show=9):
        '''
        初始化一个自定义的分页实例
        :param books_count:总的数据量
        :param per_page:每一个显示多少条数据
        :param url_prefix:分页中a标签的url
        :param max_show:页面上最多显示多少个页码
        '''
        self.current_page = current_page
        self.books_count = books_count
        self.url_prefix = url_prefix
        self.per_page = per_page
        self.max_show = max_show

        # 获取url中page 参数
        # current_page = request.GET.get("page")
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1
        # 允许每页显示多少条数据,此处为10条
        per_page = 10
        # 页码最多显示多少个页码
        max_show = 9
        # 最多显示页码数一半, / 结果显示浮点数 , // 结果显示整数
        half_show = max_show // 2
        # 计算当前数据需要显示多少页
        page_count, more = divmod(books_count, per_page)

        if more:
            page_count += 1
        if current_page > page_count:
            current_page = page_count
        self.current_page = current_page
        self.page_count = page_count
        # 计算显示页码的起点和终点
        show_page_start = current_page - half_show
        show_page_end = current_page + half_show

        # 特殊判断,当前页码 - half_show <= 0 时
        if current_page - half_show <= 0:
            show_page_start = 1
            show_page_end = max_show
        # 当前页码 + half_show >= page_count
        if current_page + half_show >= page_count:
            show_page_start = page_count - max_show + 1
            show_page_end = page_count
        # 总共需要的页码数 < max_show
        if page_count < max_show:
            show_page_start = 1
            show_page_end = page_count

        self.show_page_start = show_page_start
        self.show_page_end = show_page_end

    @property
    def start(self):
        # 每页数据的起始和终止点
        return (self.current_page - 1) * self.per_page
    @property
    def end(self):
        return self.current_page * self.per_page

    # 分页html 代码
    def page_html(self):
        tmp = []
        page_html_start = '<nav aria-label="Page navigation" class="text-center"><ul class="pagination">'
        page_html_end = '</ul></nav>'
        tmp.append(page_html_start)
        # 添加一个首页
        tmp.append('<li><a href="/{}/?page=1">首页</a></li>'.format(self.url_prefix))
        # 添加一个上一页
        # 当 当前页是第一页,不能点击上一页按钮
        if self.current_page == 1:
            tmp.append(
                '<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
        else:
            tmp.append('<li><a href="/{1}/?page={0}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(self.current_page - 1,self.url_prefix))

        # for循环添加要展示的页码
        for i in range(self.show_page_start, self.show_page_end + 1):
            # 如果for循环的当前页码等于当前页码,给L标签添加active
            if self.current_page == i:
                tmp.append('<li class="active"><a href="/bk_list/?page={0}">{0}</a></li>'.format(i))
            else:
                tmp.append('<li><a href="/{1}/?page={0}">{0}</a></li>'.format(i,self.url_prefix))
        # 添加一个下一页
        # 当 当前页是最后一页,不能点击下一页按钮
        if self.current_page == self.page_count:
            print('current_page=======',self.current_page)
            print('page_count=======', self.page_count)
            tmp.append('<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>')
        else:
            tmp.append('<li><a href="/{1}/?page={0}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(self.current_page + 1,self.url_prefix))
        # 添加一个尾页
        tmp.append('<li><a href="/{1}/?page={0}">尾页</a></li>'.format(self.page_count,self.url_prefix))
        tmp.append(page_html_end)
        page_html = ''.join(tmp)

        return page_html
封装版-mypage
def booklist(request):
    # 查看所有书籍信息
    books = models.Book.objects.all()
    # 书籍的数量
    books_count = books.count()
    current_page = request.GET.get("page")
    # url_prefix = request.path_info

    page_obj = mypage.MyPage(current_page,books_count,url_prefix="bk_list")
    # 显示每页显示的数据
    data = books[page_obj.start:page_obj.end]
    page_html = page_obj.page_html()

    return render(request,'booklist.html',{'books':data,'page_html':page_html})
封装版--views.py

Django内置分页

from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

L = []
for i in range(999):
    L.append(i)

def index(request):
    current_page = request.GET.get('p')

    paginator = Paginator(L, 10)
    # per_page: 每页显示条目数量
    # count:    数据总个数
    # num_pages:总页数
    # page_range:总页数的索引范围,如: (1,10),(1,200)
    # page:     page对象
    try:
        posts = paginator.page(current_page)
        # has_next              是否有下一页
        # next_page_number      下一页页码
        # has_previous          是否有上一页
        # previous_page_number  上一页页码
        # object_list           分页之后的数据列表
        # number                当前页
        # paginator             paginator对象
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'index.html', {'posts': posts})
内置分页view部分
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul>
    {% for item in posts %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

<div class="pagination">
      <span class="step-links">
        {% if posts.has_previous %}
            <a href="?p={{ posts.previous_page_number }}">Previous</a>
        {% endif %}
          <span class="current">
            Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
          </span>
          {% if posts.has_next %}
              <a href="?p={{ posts.next_page_number }}">Next</a>
          {% endif %}
      </span>

</div>
</body>
</html>
内置分页HTML

 中间件

中间件介绍

中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。

但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。

说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定时间去执行这些方法。

自定义中间件

在项目下的app中,写一个中间件的类

from django.utils.deprecation import MiddlewareMixin


class RBACMiddleware(MiddlewareMixin):

    white_list = ['/login/',]
    black_list = ['/black',]

    def process_request(self,request):
        next = request.path_info

        if next in self.white_list or request.session.get("login"):
            return None
        elif next in self.black_list:
            return HttpResponse('gun')
        else:
            return redirect('/login/?next={}'.format(next))
        
middleware

在settings.py中注册中间件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...

    'middleware.中间件类名',    #中间件注册
]

 中间件的五种用法

中间件可以定义五个方法,分别是(主要是process_request和process_response)
1. process_request(self,request)
2. process_view(self,request,view_func,view_args,view_kwargs)
3. process_template_response(self,request,response)
4. process_exception(self,request,exception)
5. process_response(self,request,response)

以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照Django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户

process_request

主要记忆:执行时间、执行顺序、参数和返回值
1. process_request(self,request)
   1. 执行时间
      在执行视图函数之前执行
   2. 执行顺序
      按照注册的顺序执行
   3. 参数和返回值
      1. request参数和视图函数中是同一个对象
      2. 返回值:
         1. 返回None:请求继续往后执行
         2. 返回响应对象:请求就结束了,要返回响应了

process_response

process_response(self, request, response)        
1. 执行时间
   视图函数执行之后(拿到响应对象之后)
                    
2. 执行顺序
   按照注册的倒序执行
3. 参数和返回值
   1. 参数:request请求对象
      response:响应对象
   2. 返回值:
      只能返回响应对象
      1. 返回默认的
      2. 自己生成一个响应对象返回    

process_view

process_view(self, request, view_func, view_args, view_kwargs)
1. 执行时间
   视图函数之前,在urls.py找到将要执行的视图函数之后
2. 执行顺序
   注册的顺序执行
                        
3. 参数和返回值
   1. 参数:
      1. request: 请求对象
      2. view_func:将要执行的视图函数
   2. 返回值:
      1. 返回None:继续往后执行
      2. 返回响应对象,直接跳出,按照process_response方法的顺序执行
                

练习:

限制用户访问频率,一分钟只允许访问3次

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,render,redirect

import time
D = {}
Whitelist = ['/test/']
class Xianzhi(MiddlewareMixin):
    def process_request(self,request):
        # 限制访问频率
        # 当前访问ip
        ip = request.META.get("REMOTE_ADDR")
        now = time.time()

        if request.path_info in Whitelist:
            return None

        if ip not in D:
            D[ip] = []

        # 拿到当前ip的访问历史记录
        history = D[ip]
        while history and now - history[-1] > 60:
            history.pop()
        if len(history) >= 3:
            return HttpResponse('gun')
        else:
            history.insert(0,now)
访问频率限制功能

 

使用中间件用于登录验证

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'mymiddleware.LoginCheckMD1',
]
settings.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse,redirect


# 定义一个登陆验证的中间件
class LoginCheckMD1(MiddlewareMixin):
    white_list = ['/login/',]
    black_list = ['/black',]

    def process_request(self,request):
        next = request.path_info

        if next in self.white_list or request.session.get("login"):
            return None
        elif next in self.black_list:
            return HttpResponse('gun')
        else:
            return redirect('/login/?next={}'.format(next))
中间件登录验证

 

posted @ 2018-08-20 15:43  叫你你敢答应么  阅读(136)  评论(0)    收藏  举报