如何使用基于 Django 类的通用 ListViews 的分页?

Posted

技术标签:

【中文标题】如何使用基于 Django 类的通用 ListViews 的分页?【英文标题】:How do I use pagination with Django class based generic ListViews? 【发布时间】:2011-08-19 22:30:40 【问题描述】:

如何在 Django 1.3 中使用分页?

文档对此不是很清楚。

我的views.py 怎么了?

我的模板有什么用?

我的 URLconf 文件中有什么内容?

【问题讨论】:

也许这篇博文会有所帮助:dontrepeatyourself.org/post/… 【参考方案1】:

假设,我在 app/models.py 中有一个名为 FileExam(models.Model) 的类:

app/models.py

class FileExam(models.Model):
    myfile = models.FileField(upload_to='documents/%Y/%m/%d')
    date = models.DateTimeField(auto_now_add=True, blank=True)
    teacher_name = models.CharField(max_length=30)
    status = models.BooleanField(blank=True, default=False)

app/views.py

from app.models import FileExam
from django.core.paginator import Paginator
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger

class FileExamListView(ListView):
    model = FileExam
    template_name = "app/exam_list.html"
    paginate_by = 10
    
    def get_context_data(self, **kwargs):
        context = super(FileExamListView, self).get_context_data(**kwargs) 
        list_exam = FileExam.objects.all()
        paginator = Paginator(list_exam, self.paginate_by)

        page = self.request.GET.get('page')

        try:
            file_exams = paginator.page(page)
        except PageNotAnInteger:
            file_exams = paginator.page(1)
        except EmptyPage:
            file_exams = paginator.page(paginator.num_pages)
            
        context['list_exams'] = file_exams
        return context

get_context_data 只做了一点改动,并添加了来自 django 文档here 的分页代码

app/templates/app/exam_list.html

正常内容列表

<table id="exam">
  % for exam in list_exams %
  <tr>
    <td> exam.myfile </td>
    <td> exam.date </td>
    <td>.....</td>
  </tr>
  % endfor %
</table>

分页

% if is_paginated %
<ul class="pagination">
% if page_obj.has_previous %
    <li>
        <span><a href="?page= page_obj.previous_page_number ">Previous</a></span>
    </li>
% endif %
    <li class="">
        <span>Page  page_obj.number  of  page_obj.paginator.num_pages .</span>
    </li>
% if page_obj.has_next %
    <li>
        <span><a href="?page= page_obj.next_page_number ">Next</a></span>
    </li>
% endif %
</ul>
% else %
    <h3>Your File Exam</h3>
    <p>File not yet available</p>
% endif %

app/urls.py

urlpatterns = [
url(
    r'^$', views.FileExamListView.as_view(), name='file-exam-view'),
), 
... ]

【讨论】:

这看起来不对:context = super(SoalListView, self)...。您的意思是:context = super(FileExamListView, self)... 是的,没错!此答案已由 Yacin 先生编辑。谢谢你,亚辛先生。 根据我的测试,我们不能像你一样拦截PageNotAnInteger 异常,因为如果我们通过GET 参数传递一个非整数ValueError 异常将在之前引发PageNotAnInteger 异常。这发生在ListView 类的paginate_queryset 方法级别。希望这很清楚【参考方案2】:

我们有两种方法可以做到这一点。

第一个很简单,只需设置类字段paginate_byget_context_data 方法不需要我们做任何事情。

第二种方法有点复杂,但是我们可以对分页有更多的了解,并自定义复杂的分页或多个分页。来看看吧。

可以分三步完成。

1.重写Viewget_context_data方法。

传递 page_keyspages 以便我们可以迭代列表并避免硬编码。

def get_context_data(self, *, object_list=None, **kwargs):
    context = super().get_context_data()
    df = pd.DataFrame(list(self.model.objects.all().values()))
    ipc = df.groupby('ip')['ip'].count().sort_values(ascending=False)
    urlc = df.groupby('url')['url'].count().sort_values(ascending=False).to_dict()

    ipc = tuple(ipc.to_dict().items())
    urlc = tuple(urlc.items())

    pages = []
    page_keys = ['page1', 'page2']
    for obj, name in zip([urlc, ipc], page_keys):
        paginator = Paginator(obj, 20)
        page = self.request.GET.get(name)
        page_ipc = obj
        try:
            page_ipc = paginator.page(page)
        except PageNotAnInteger:
            page_ipc = paginator.page(1)
        except EmptyPage:
            page_ipc = paginator.page(paginator.num_pages)
        pages.append(page_ipc)

    context['data'] = zip(pages, page_keys)
    return context

2.自定义您的子template

我们定义了一些变量,以便我们可以遍历分页列表。

pagination.html

    % if is_paginated %
        <ul class="pagination">
        % if page_obj.has_previous %
            <li>
            <span><a href="? pname = page_obj.previous_page_number ">Previous</a></span>
            </li>
        % endif %
        <li class="">
            <span>Page  page_obj.number  of  page_obj.paginator.num_pages .</span>
        </li>
        % if page_obj.has_next %
            <li>
            <span><a href="? pname = page_obj.next_page_number ">Next</a></span>
            </li>
        % endif %
        </ul>
    % else %
        <h3>Your File Exam</h3>
        <p>File not yet available</p>
    % endif %

3.自定义外层template

index.html

% for foo,name in data %
    <div class="col-md-3 table-responsive">

            % for k,v in foo %
                <tr>
                    <th> forloop.counter </th>
                    <td> k </td>
                    <td> v </td>
                </tr>
            % endfor %

        % include 'pagination.html' with pname=name  page_obj=foo %
    </div>
% endfor %

【讨论】:

【参考方案3】:

我想您会询问有关在基于类的新视图中使用分页的信息,因为使用传统的基于函数的视图很容易找到。我发现只需设置paginate_by 变量就足以激活分页。请参阅Class-based generic views

例如,在您的views.py:

import models
from django.views.generic import ListView

class CarListView(ListView):
    model = models.Car      # shorthand for setting queryset = models.Car.objects.all()
    template_name = 'app/car_list.html'  # optional (the default is app_name/modelNameInLowerCase_list.html; which will look into your templates folder for that path and file)
    context_object_name = "car_list"    #default is object_list as well as model's_verbose_name_list and/or model's_verbose_name_plural_list, if defined in the model's inner Meta class
    paginate_by = 10  #and that's it !!

在您的模板 (car_list.html) 中,您可以包含这样的分页部分(我们有一些可用的上下文变量:is_paginatedpage_objpaginator)。

# .... **Normal content list, maybe a table** .... #
% if car_list %
    <table id="cars">
        % for car in car_list %
            <tr>
                <td> car.model </td>
                <td> car.year </td>
                <td><a href="/car/ car.id /" class="see_detail">detail</a></td>
            </tr>
        % endfor %
    </table>
    # .... **Now the pagination section** .... #
    % if is_paginated %
        <div class="pagination">
            <span class="page-links">
                % if page_obj.has_previous %
                    <a href="/cars?page= page_obj.previous_page_number ">previous</a>
                % endif %
                <span class="page-current">
                    Page  page_obj.number  of  page_obj.paginator.num_pages .
                </span>
                % if page_obj.has_next %
                    <a href="/cars?page= page_obj.next_page_number ">next</a>
                % endif %
            </span>
        </div>
    % endif %
% else %
    <h3>My Cars</h3>
    <p>No cars found!!! :(</p>
% endif %
# .... **More content, footer, etc.** .... #

要显示的页面由 GET 参数指示,只需将 ?page=n 添加到 URL。

【讨论】:

没关系,但是你如何绑定模板也看到“car_list”对象? 仅供参考,您也可以直接在 urls.py:url(r'^cars/$', ListView.as_view( model=Car, paginate_by=10 )) 中执行此操作, 我学到的教训:要找到一种方法,在新选项卡中打开所有祖先类,然后 CTRL+F 去掉关键字。所以从docs.djangoproject.com/en/dev/ref/class-based-views/…,我们从基础教程中知道存在,打开所有祖先链接并搜索“pagi” 我一直在这样做,但我发现的问题是,当我对查询集中的对象进行额外处理时,它会将它们应用于数据库中的所有结果。因此,对于返回 100 个对象但每页仅显示 10 个对象的查询,将对 100 个对象进行额外处理。 我不喜欢硬编码的网址,您可以将其替换为:previous

以上是关于如何使用基于 Django 类的通用 ListViews 的分页?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据 Django 中当前基于类的通用视图模型向模板加载器添加路径

基于 Django 类的通用视图和 ModelForms

Django 基于类的通用视图和身份验证

基于 Django 类的视图和通用视图详细信息使用

Django - 基于类的通用视图 - “没有要重定向到的 URL”

从 Django 中基于类的通用视图自定义表单变量的首选方法是啥?