【Django】分页
分页(pagination)用于将数据划分为多个页,从而可以在查看数据列表时使用“上一页/下一页”链接。
官方文档:
- https://docs.djangoproject.com/en/stable/topics/pagination/
- https://docs.djangoproject.com/en/stable/ref/paginator/
1.Paginator类
在Django中分页使用Paginator
类,主要功能是将对象列表(可以是列表、元组、QuerySet
等)划分为Page
对象。
创建Paginator
对象时指定对象列表和每一页包含的对象数量,即可访问每一页包含的对象。
示例:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
range(1, 3)
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
>>> page1.number
1
>>> len(page1)
2
>>> page1[1]
'paul'
>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
django.core.paginator.EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index()
3
>>> page2.end_index()
4
>>> p.page(0)
Traceback (most recent call last):
...
django.core.paginator.EmptyPage: That page number is less than 1
>>> p.page(3)
...
django.core.paginator.EmptyPage: That page contains no results
注意:页码和对象索引都从1开始。
2.ListView使用分页
通用视图ListView
提供了内置的分页方式,只需在视图类中添加paginate_by
(页大小)属性即可:
1
2
3
4
5
6
7
from django.views.generic import ListView
from myapp.models import Contact
class ContactListView(ListView):
paginate_by = 2
model = Contact
该属性将限制每一页的对象数量,并在返回的context
中添加paginator
, page_obj
和object_list
,分别表示使用的分页器、当前页对象和当前页包含的对象列表(object_list
, page_obj.object_list
以及原有的<model>_list
都是同一个QuerySet
)。从而可以在模板中添加“上一页/下一页”链接:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{% for contact in page_obj %}
{# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br>
...
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ paginator.num_pages }}">last »</a>
{% endif %}
</span>
</div>
当前页码由GET参数page
指定(默认为1),返回的page_obj
就是指定页码对应的页,因此指向其他页的超链接只需增加page
参数。
这一逻辑由ListView
自动完成,不需要手动处理。
3.视图函数使用分页
1
2
3
4
5
6
7
8
9
10
11
from django.core.paginator import Paginator
from django.shortcuts import render
from myapp.models import Contact
def listing(request):
contact_list = Contact.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page.
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'list.html', {'page_obj': page_obj})
4.示例:生成分页栏
Paginator
类提供了一个方便的方法get_elided_page_range()
返回页码序列,当页数过多时会自动使用省略号省略中间页,只保留开头、结尾和当前页左右的页码。
1
2
3
4
>>> from django.core.paginator import Paginator
>>> p = Paginator(list(range(1000)), 20)
>>> list(p.get_elided_page_range(10))
[1, 2, '…', 7, 8, 9, 10, 11, 12, 13, '…', 49, 50]
其中'…'
是类常量Paginator.ELLIPSIS
。
利用该方法和Bootstrap样式在模板中生成分页栏:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{% if is_paginated %}
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item"><a href="?page={{ page_obj.previous_page_number }}" class="page-link">上一页</a></li>
{% endif %}
{% for p in page_range %}
{% if p == paginator.ELLIPSIS %}
<li class="page-item disabled"><a href="#" class="page-link">...</a></li>
{% elif p == page_obj.number %}
<li class="page-item active"><a href="?page={{ p }}" class="page-link">{{ p }}</a></li>
{% else %}
<li class="page-item"><a href="?page={{ p }}" class="page-link">{{ p }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a href="?page={{ page_obj.next_page_number }}" class="page-link">下一页</a></li>
{% endif %}
</ul>
{% endif %}
其中page_range
需要在视图中手动添加到context
:
1
2
3
4
5
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
page = context['page_obj']
context['page_range'] = context['paginator'].get_elided_page_range(page.number)
return context
注意:如果当前url包含其他的GET参数(例如搜索的关键词)则也需要添加到context
并写在href
属性中。
效果:
This post is licensed under CC BY 4.0 by the author.