Django 博客 - 8 分页

前言

经过了上一篇添加侧边栏之后,我们的博客主页就基本有个博客样子了,但是博客主页会将所有的文章一次性显示出来,这就不太科学了,所以需要对文章进行分页,同时添加一个分页导航栏

Paginator和Page

Paginator是分页器,可以将一个列表,按要求分页,而Page就是其中的一页

from blog.models import Post
from django.core.paginator import Paginator

posts = Post.objects.all()
#将posts分页,每页有10篇文章,假设有100篇post,这里就会分成10页
paginator = Paginator(posts, 10)

paginator.page_range 
#页码范围 range(1, 11)

paginator.num_pages
#页数 10

paginator.object_list
#所以文章 就是posts

page = paginator.page(2)
#返回的是Page对象,参数2是第几页

page.paginator
#返回的是Paginator对象

page.object_list
#当前页的对象列表

page.number
#当期的页码 2

page.has_next()
page.has_previous()
page.has_other_pages()
page.next_page_number()
page.previous_page_number()
#判断是否有上下一页以及对应的页码
#还有其他的一些用法没列出来,可以查看官方文档

在ListView里使用paginator

只要在ListView里定义一个paginate_by,就可以使用分页了
比如在IndexView里指定paginate_by = 8,这样每一页就只有8篇文章了

class IndexView(ListView):
    model = Post
    context_object_name = 'posts'
    template_name = 'blog/index.html'
    paginate_by = 8

定义了paginate_by之后,模板上下文就多了三个变量paginatorpage_objis_paginated
paginator就是Paginator对象,page_obj是Page对象,is_paginated用来判断是否需要分页,如果只有一页就不用分页了

有了这几个变量之后,最简单的就是直接在模板里添加

{% if is_paginated %}
<ul>
    {% for page in paginator.page_range %}
        <li><a href="?page={{ page }}">{{ page }}</a></li>
    {% endfor %}
</ul>
{% endif %}

这里的超链接url?page=页码,点击就会显示对应页码的页面是因为ListView已经对这个查询字符串做了处理
只要定义了paginate_by,就会去解析page参数,page参数名可以通过page_kwarg修改
具体实现源码可以查看MultipleObjectMixin

同时当前页码的文章列表不再从posts里获取,而是通过page_obj.object_list获取

美化分页

上面的模板代码有个弊端,如果文章很多的话,页面范围就很大,分页组件显示出来也非常的长
可以将中间的页码用省略号来表示,然后根据当前页面动态更新分页组件
但是如果在模板中加这些判断代码的话,会很难实现,因此可以用模板标签来实现

我们要实现的是如图所示,两端至少2个可见页码,当前页码两侧至多3个可见页码
pagination
这里使用一个新的自定义标签装饰器inclusion_tag,可以直接返回HTML,而不是一堆模板变量
inclusion_tag装饰器需要制定模板文件,同时将takes_context设置为True,可以自动获取标签所在模板的上下文,这样就不用自己给标签传递参数了

新建blog/templatetags/paginator.py,内容如下

from django import template

register = template.Library()

@register.inclusion_tag('blog/paginator.html', takes_context=True)
def pagination(context, *args, **kwargs):
    paginator = context['paginator']
    page_obj = context['page_obj']
    is_paginated = context['is_paginated']

    page_num = page_obj.number

    pagination_required = is_paginated
    if not pagination_required:
        page_range = []
    else:
        ON_EACH_SIDE = 3
        ON_ENDS = 2

        if paginator.num_pages <= 10:
            page_range = paginator.page_range
        else:
            page_range = []
            if page_num > (ON_EACH_SIDE + ON_ENDS + 1):
                page_range.extend(range(1, ON_ENDS + 1))
                page_range.append(None)
                page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
            else:
                page_range.extend(range(1, page_num + 1))

            if page_num < (paginator.num_pages - ON_EACH_SIDE - ON_ENDS + 1):
                page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
                page_range.append(None)
                page_range.extend(range(paginator.num_pages - ON_ENDS + 1, paginator.num_pages + 1))
            else:
                page_range.extend(range(page_num + 1, paginator.num_pages + 1))

    return {
        'pagination_required': pagination_required,
        'page_range': page_range,
        'page_num': page_num,
        'page_obj': page_obj,
    }

我们是在ListView的模板里使用这个标签的,所以当takes_context=True时,就可以通过context,得到paginatorpage_objis_paginated
这个函数主要作用是当页数小于10页,不对页码范围做处理,当大于10页时,会按情况省略掉中间的页面,并用一个None表示
这个函数返回的是一个字典,这个字典可以在inclusion_tag的模板里面使用,接下来编写这个模板文件

扫描二维码关注公众号,回复: 475538 查看本文章

新建blog/templates/blog/paginator.html文件,内容如下

{% if pagination_required %}
    <ul>
        {% if page_obj.has_previous %}
            <li><a href="?page={{ page_obj.previous_page_number }}">previous</a></li>
        {% else %}
            <li><a>previous</a></li>
        {% endif %}

        {% for page in page_range %}
            {% if page is not None %}
                {% if page == page_num %}
                    <li><span>{{ page }}</span></li>
                {% else %}
                    <li><a href="?page={{ page }}">{{ page }}</a></li>
                {% endif %}
            {% else %}
                <li><span>...</span></li>
            {% endif %}
        {% endfor %}

        {% if page_obj.has_next %}
            <li><a href="?page={{ page_obj.next_page_number }}">next</a></li>
        {% else %}
            <li><a>next</a></li>
        {% endif %}
    </ul>
{% endif %}

根据是否有上下一页添加对应的超链接。根据页码的值是否为None,是的话就显示...
至此,美化后的分页组件也写好了,只需要在主页的模板里添加如下两句,就可以显示分页组件了

{% load paginator %}
{% pagination %}

扩展阅读

inclusion-tags的用法

猜你喜欢

转载自blog.csdn.net/abc_1234d/article/details/78343436
今日推荐