User-Friendly Django Pagination
Tags: django, python, webdev, ux
Published: 2023-12-31 18:02:26

Pagination is a common practice in web development used to divide large datasets into smaller and more manageable parts displayed on multiple pages.

Let's consider a successful example of pagination in Django using the **`Paginator`** class. This type of pagination is user-friendly and usually suitable for most cases.


![Pagination UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ayaihhfc1yp9g1uxpe6g.jpg)

This type of pagination allows you to:

- Move to the next and previous pages using the **Prev** and **Next** buttons;
- Move to the first and last pages from any page;
- Navigate by skipping one page at a time.

## Pagination in Django

You can find detailed information about pagination in Django from the following links:

- [Documentation: Pagination](https://docs.djangoproject.com/en/5.0/topics/pagination/)
- [Pagination for a User-Friendly Django App](https://realpython.com/django-pagination/)

In summary, pagination in Django is accomplished using the `Paginator` and `Page` classes. The `Paginator` class divides a large dataset into pages, taking a set of objects (`list`, `tuple`, or `QuerySet`) and the page size as parameters. The `Page` class represents the current page in pagination and contains the page objects and information about neighboring pages.

You can create a paginator manually or automatically using Class-Based Views. The template is passed the page object (`page_obj`), which contains the objects, page information, and the paginator itself.

**Attributes used in the presented pagination:**

- `page_obj.has_previous`: Checks if there is a previous page.
- `page_obj.has_next`: Checks if there is a next page.
- `page_obj.previous_page_number`: Returns the number of the previous page.
- `page_obj.next_page_number`: Returns the number of the next page.
- `page_obj.number`: Returns the number of the current page.
- `page_obj.paginator`: Returns an instance of the `Paginator` class.
- `page_obj.paginator.num_pages`: Returns the total number of pages.
- `page_obj.paginator.page_range`: Returns the range of pages.

The presented pagination also uses the **add** template filter, which allows adding values to variables directly in the template.

## User-Friendly Pagination

The screenshot shows how the paginator will look on different pages. The paginator always displays the extreme pages and two neighboring pages, if they exist. The **Prev** and **Next** buttons are also displayed if the current page is not an extreme page.


![Paginator on different pages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4c1gc59izanit62haf1.png)

The following listing shows the implementation of this pagination. Simply insert this code into your project, and it should work. We will examine this listing in more detail.

```html
{% if page_obj.has_previous %}
  <li><a href="?page={{ page_obj.previous_page_number }}">(Prev)</a></li>

  {% if page_obj.number > 3 %}
      <li><a href="?page=1">(1)</a></li>
    {% if page_obj.number > 4 %}
      <li><a >...</a></li>
    {% endif %}
  {% endif %}
{% endif %}

{% for num in page_obj.paginator.page_range %}
  {% if page_obj.number == num %}
      <li><a href="?page={{ num }}">({{ num }})</a></li>
  {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
      <li><a href="?page={{ num }}">({{ num }})</a></li>
  {% endif %}
{% endfor %}

{% if page_obj.has_next %}
  {% if page_obj.number < page_obj.paginator.num_pages|add:'-3' %}
    <li><a >...</a></li>
    <li><a href="?page={{ page_obj.paginator.num_pages }}">
      ({{ page_obj.paginator.num_pages }})
    </a></li>
  {% elif page_obj.number < page_obj.paginator.num_pages|add:'-2' %}
    <li><a href="?page={{ page_obj.paginator.num_pages }}">
      ({{ page_obj.paginator.num_pages }})
    </a></li>
  {% endif %}

  <li><a href="?page={{ page_obj.next_page_number }}">(Next)</a></li>
{% endif %}

```

This code snippet is a Django template for creating a pagination control element. Let's go through what each part of this code does:

##### 1. Link to Previous Page:
```html
{% if page_obj.has_previous %}
  <li><a href="?page={{ page_obj.previous_page_number }}">(Prev)</a></li>
```
If `page_obj.has_previous` is `True`, it shows the **Prev** button to go to the previous page. The variable `page_obj.previous_page_number` contains the number of the previous page.

##### 2. Links to First Pages and Ellipsis:
```html
{% if page_obj.number > 3 %}
    <li><a href="?page=1">(1)</a></li>
  {% if page_obj.number > 4 %}
    <li><a >...</a></li>
  {% endif %}
{% endif %}
```
These lines of code handle the display of a link to the first page and an ellipsis (`...`) if the current page number is greater than 3 (or 4 for the ellipsis).

##### 3. Current Page and Links to Neighboring Pages:
```html
{% for num in page_obj.paginator.page_range %}
  {% if page_obj.number == num %}
      <li><a href="?page={{ num }}">({{ num }})</a></li>
  {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
      <li><a href="?page={{ num }}">({{ num }})</a></li>
  {% endif %}
{% endfor %}
```
This loop goes through the page range in the paginator. It checks two conditions:

- If the page number (`num`) is the current page (`page_obj.number`), it creates a link for it. Typically, this is styled differently to indicate the current page.
- If the page number is within three pages before or after the current page, it also creates links to these pages. This allows the user to navigate to the nearest two pages without viewing the entire range.

##### 4. Ellipsis and Link to Last Page:
```html
{% if page_obj.has_next %}
  {% if page_obj.number < page_obj.paginator.num_pages|add:'-3' %}
    <li><a >...</a></li>
    <li><a href="?page={{ page_obj.paginator.num_pages }}">
      ({{ page_obj.paginator.num_pages }})
    </a></li>
  {% elif page_obj.number < page_obj.paginator.num_pages|add:'-2' %}
    <li><a href="?page={{ page_obj.paginator.num_pages }}">
      ({{ page_obj.paginator.num_pages }})
    </a></li>
  {% endif %}
```
This part is similar to the second section, but for the end of the pagination range. It displays an ellipsis and a link to the last page if the current page is several pages away from the last page.

##### 5. Link to Next Page:
```html
{% if page_obj.has_next %}
...
<li><a href="?page={{ page_obj.next_page_number }}">(Next)</a></li>
{% endif %}
```

Similar to the first section, but for the button to go to the next page.

## Conclusion

We have explored an example of a simple and user-friendly pagination in Django that allows users to navigate through pages and access the desired information easily.

The pagination implementation can be adapted to your needs, and you can make changes to the code to fit your project requirements. Good luck using pagination in your Django applications!