【问题标题】:Django: paginating differently on the first pageDjango:在第一页上以不同的方式分页
【发布时间】:2010-11-06 12:00:11
【问题描述】:

目前,我正在使用 Django 的 object_list 来处理分页。如果您在听到我的问题后认为我需要它,我很乐意转到正确的 Paginator() 类:

在主页上,我想按 7 分页,但在所有其他页面上,我想按 10 分页。

我该怎么做呢?我真的无法理解它。我最接近让它工作的结果导致一整页的结果被遗漏了,所以显然我不希望这样。

我会非常感谢任何答案。如果您需要更多信息,请告诉我。非常感谢。

【问题讨论】:

    标签: django pagination


    【解决方案1】:

    只需from django.core.paginator import Paginator 并在主页视图中将分页器对象设置为p = Paginator(thestuff, 7),在其他任何地方创建p = Paginator(thestuff, 10)。然后在任何一种情况下,在用于呈现模板的上下文中绑定 pp.object_list 在任何一种情况下都会被适当地设置(你似乎说这就是你正在使用的方法,对吧?也就是说,这就是你所说的“Django's object_list”的意思吗?)。

    Django docs 有很好的细节和示例(假设您使用的是 1.0 或更高版本)。如果你不能让它工作,你能告诉我们(一个仍然失败的简化版本)你的模板和视图代码吗?

    编辑:问题现在已经很清楚了,我认为应该通过子类化 Django 的核心分页器来解决,如下:

    from django.core.paginator import Paginator, Page
    
    class MyPaginator(Paginator):
    
      def __init__(self, **kw):
        self.deltafirst = kw.pop('deltafirst', 0)
        Paginator.__init__(self, **kw)
    
      def page(self, number):
        "Returns a Page object for the given 1-based page number."
        number = self.validate_number(number)
        if number == 1:
          bottom = 0
          top = self.per_page - self.deltafirst
        else:
          bottom = (number - 1) * self.per_page - self.deltafirst
          top = bottom + self.per_page
        if top + self.orphans >= self.count:
          top = self.count
        return Page(self.object_list[bottom:top], number, self)
    

    现在完全按照上面的文本使用 MyPaginator,示例显示了 Django 自己的用法,除了在创建它时,使用额外的命名参数deltafirst=3 使第一页 3 比正常的每页长度短( 10)。因此,您将使用标称长度为 10 但 deltafirst 为 3 的单个分页器,以使第一页 3 比所有其他页面短。

    validate_number 中可能存在问题,但我不确定它们是否会出现——如果出现了,那么 MyPaginator 也需要覆盖该方法。

    最新版本的 Django 更新

    由于没有考虑位置参数,上述代码会出错,并且还需要覆盖 num_pages 以允许正确显示最后一页:

    from math import ceil
    
    
    class MyPaginator(Paginator):
        
        def __init__(self, *args, **kw):
            self.deltafirst = kw.pop('deltafirst', 0)
            super().__init__(*args, **kw)
        
        def page(self, number):
            number = self.validate_number(number)
            if number == 1:
                bottom = 0
                top = self.per_page - self.deltafirst
            else:
                bottom = (number - 1) * self.per_page - self.deltafirst
                top = bottom + self.per_page
            if top + self.orphans >= self.count:
                top = self.count
            return self._get_page(self.object_list[bottom:top], number, self)
    
        @property
        def num_pages(self):
            if self.count == 0 and not self.allow_empty_first_page:
                return 0
            count = max(self.count - self.per_page + self.deltafirst, 0)
            hits = max(0, count - self.orphans)
            return 1 + ceil(hits / self.per_page)
    

    【讨论】:

    • 嗨,亚历克斯。感谢您的建议,但它有一个问题。如果您有时间,请查看pastie.org/527183,它显示了当我使用类似于您建议的方法时会发生什么。基本上,Django 以正确的数量分页,但在第 2 页上,它忽略了它认为在第 1 页上显示的行(即使它们没有使用不同的 paginate_by 数量)。抱歉,这很难解释,但从 Pastie 链接中应该可以理解。谁有办法解决这个问题?
    • 是的,看到了粘贴链接,它确实有意义——所以我编辑了我的答案以提供一个解决方案(Django 1.0 核心 Paginator 的调整子类)。
    • 完美!感谢您花时间写。乔。
    【解决方案2】:

    object_list() 函数使用分页器来拆分列表。 所以,如果你改变块的“大小”(7 对 8)......那么它就会如你所见。

    选择: 最后的选择是靠不住的。不要那样做 :-) 要么“破解”它,要么转移到 Paginator。

    Hack it ... 在请求 > 第 1 页上,将一些额外的东西偷偷带入 row_list。我猜你可以通过 list(row_list) 将它强制放入一个列表,并通过 row_list.insert(0, None)

    预先设置一个 None 或其他东西

    分页 ...使用分页器

    视图 + 模板 ...让您的视图只需将查询集 row_list 传递给模板。对第 1 页进行模板检查。您是否拥有 row_list 子集的循环/显示。在你的模板中有这样的逻辑是很糟糕的。它构成了一个复杂的模板。

    【讨论】:

    • 感谢您的回答。我在 Python shell 中测试了“Hack It”方法,它运行良好,因此绝对有可能解决这个问题。乔。
    【解决方案3】:

    我意识到这是一个老问题,但万一有人像我一样来到这里发现我的单元测试发现了一个问题。

    Alex 基本上是对的,但不要忘记同时实现 _get_num_pagesself.orphans + self.deltafirst,否则最后一页将不正确。

    def _get_num_pages(self):
        """Returns the total number of pages."""
        if self._num_pages is None:
            if self.count == 0 and not self.allow_empty_first_page:
                self._num_pages = 0
            else:
                hits = max(1, self.count - self.orphans + self.deltafirst)
                self._num_pages = int(ceil(hits / float(self.per_page)))
        return self._num_pages
    num_pages = property(_get_num_pages)
    

    还有单元测试:

    def test_ads_paginator(self):
        per_page = 10
        num_items = 100
        num_firstpage = 5
        expected_num_pages = 11
    
        items = range(num_items)
        paginator = AdsPaginator(items, per_page, deltafirst=per_page-num_firstpage)
        last_item = paginator.page(paginator.num_pages).object_list[-1]
    
        self.assertEqual(len(paginator.page(1)), num_firstpage)
        self.assertEqual(paginator.num_pages, expected_num_pages)
        self.assertEqual(last_item, items[-1])
    

    【讨论】:

      【解决方案4】:

      对于我的特定场景,我希望在 1 个呈现的模板中对对象进行分页(创建一个多页文档)。就我而言,我认为最简单的方法是忘记分页器并改用生成器。

          def pages_iter():
              page_num = 1
              num_records = lambda x: 30 if x > 1 else 10
              end_idx = 0
      
              while True:
                  start_idx = end_idx
                  end_idx += num_records(page_num)
                  yield {
                      'number': page_num,
                      'object_list': donors[start_idx:end_idx]
                  }
      
                  if end_idx > donors.count() - 2:
                      break
                  page_num += 1
      

      { pages: page_iter } 添加到您的页面上下文中,然后您就拥有了一个变量分页器。请注意,num_records 是一个接受 page_number 的函数,因此您确实拥有极大的灵活性。

      在您的模板中,您可以像处理分页器一样处理它:

      {% for page in pages %}
        {% if page.number == 1 %}
          {{ header }}
        {% endif %}
      
        {% for obj in page.object_list %}
          {{ obj }}
        {% endfor %}
      {% endfor %}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-07
        • 2020-12-19
        • 1970-01-01
        • 1970-01-01
        • 2022-01-27
        • 2018-01-24
        • 2019-01-02
        • 1970-01-01
        相关资源
        最近更新 更多