Django: paginating differently on the first page

django, pagination

Solution

Just `from django.core.paginator import Paginator` and make a paginator object as `p = Paginator(thestuff, 7)` in the view for the homepage, `p = Paginator(thestuff, 10)` everywhere else. Then in either case bind `p` in the context you use to render the template. `p.object_list` will be set appropriately in either case (and you appear to say that's the approach you're using, right? I.e., is that what you mean by "Django's object_list"?).

Django docs have excellent details and examples (assuming you're on 1.0 or better). If you can't make it work, can you show us (a simplified version that still fails, of) your template and view code?

Edit: problem has now been clearly shown and I think should be solved by subclassing Django's core paginator, as follows:

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)

Now use MyPaginator exactly as the above text and examples show the usage of Django's own, except, on creating it, use an extra named argument `deltafirst=3` to make the first page 3 shorter than the normal per-page length (of 10). So you'd be using a single paginator with a nominal length of 10 but a deltafirst of 3 to make the first page 3 shorter than all others.

(There may be problems in `validate_number` but I'm not sure they'd appear -- if they do, then MyPaginator will need to override that method as well).

Update for recent versions of Django:

The above code would give an error due to not accounting the positional arguments and `num_pages` also needs to be overriden to allow the last pages to be shown properly:

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)

Problem

At the minute, I'm using Django's object_list to handle pagination. I'm happy to move to the proper Paginator() class if you think I need it after you hear my problem: On the homepage, I want to paginate by 7, but on all other pages I want to paginate by 10. How would I go about doing this? I really can't get my head around it. The closest I've got to making it work resulted in a whole page of results being left out, so obviously I don't want that. I'd be extremely appreciative of any answers. Let me know if you need any more information. Thanks a lot. Joe

Original source