Privacy Policy
Snippets index

  Django Forms: Pagination of Filtered Results

Django’s forms can be used, in conjunction with HTML forms, for pagination, using the inbuilt Django pagination features. Basically, anytime you have a URL that may have some parts of the query string that may need to be built, it’s simpler to use a form element, than to manually build up the url in your template.

Working example:

views.py

from django import forms
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, DetailView
from django.views.generic.edit import FormMixin


class NotificationRecordFilterForm(forms.Form):
    message_text = forms.CharField(label=_('Search'), required=False)

    def filter_by(self):
        return {
            'notification__message__icontains': self.cleaned_data['message_text']
        }


class NotificationsRecordList(FormMixin, ListView):

    template_name = 'frontend/pages/user/notifications.html'
    paginate_by = 10
    form_class = NotificationRecordFilterForm

    def get_form_kwargs(self):
        kwargs = {
            'initial': self.get_initial(),
            'prefix': self.get_prefix(),
            'data': self.request.GET or None,
        }
        return kwargs

    def get_queryset(self):
        queryset = NotificationRecord.objects.filter(user=self.request.user)
        form = self.get_form(self.get_form_class())
        if form.is_valid():
            queryset = queryset.filter(**form.filter_by())
        return queryset

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

user_notifications = NotificationsRecordList.as_view()


class NotificationsRecordDetail(DetailView):

    template_name = 'frontend/pages/user/notification_view.html'
    pk_url_kwarg = 'id'

    def get_queryset(self):
        queryset = NotificationRecord.objects.filter(user=self.request.user)
        return queryset

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_object(self, queryset=None):
        obj = super().get_object(queryset)
        obj.set_read()
        return obj

user_notification_view = NotificationsRecordDetail.as_view()

urls.py

from django.urls import path
from . import views

app_name='frontend'

urlpatterns = [
    ...
    path('user/notifications/', views.user_notifications, name="user_notifications"),
    path('user/notification/<uuid:id>/view/', views.user_notification_view, name="user_notification_view"),
    ...
]

templates/notifications.html

{% extends 'frontend/base.html' %}
{% load i18n static %}

{% block content %}

<div class="row">
    <div class="col-sm-9">

    ***
    *** We use the form for GET
    ***
    <form action="{% url 'frontend:user_notifications' %}">
        <section class="panel">

            <header class="panel-heading wht-bg">

                <div style="max-width: 320px; margin-top: 5px;" class="input-append input-group pull-right">

                    ***
                    *** Render search input items from form
                    ***
                    <input name="{{ form.message_text.html_name }}" value="{{ form.message_text.value|default:'' }}" class="form-control" type="search" placeholder="{% trans 'message text' %}">

                    ***
                    *** Assign page=1 to search form submit button
                    ***
                    <span class="input-group-btn">
                        <button class="btn btn-success" type="submit" name="page" value="1">{% trans 'Search' %}</button>
                    </span>
                </div>

                <h4 class="gen-case">{% trans 'Your notifications' %} ({{page_obj.paginator.count}})</h4>
            </header>

            <div class="panel-body minimal">
                <div class="mail-option">

                    ***
                    *** Sample compact pagination with prev/next buttons
                    ***
                    <ul class="unstyled inbox-pagination">
                        <li><span>{{page_obj.start_index}}-{{page_obj.end_index}} {% trans 'of' %} {{page_obj.paginator.count}}</span></li>
                        <li>
                            {# <a class="np-btn" href="#"><i class="fa fa-angle-left  pagination-left"></i></a> #}
                            <button class="np-btn"
                                {% if page_obj.has_previous %}
                                    name="page"
                                    value="{{ page_obj.previous_page_number }}"
                                    type="submit"
                                {% else %}
                                    disabled="disabled"
                                {% endif %}>
                                <i class="fa fa-angle-left  pagination-left"></i>
                            </button>
                        </li>
                        <li>
                            {# <a class="np-btn" href="#"><i class="fa fa-angle-right pagination-right"></i></a> #}
                            <button class="np-btn"
                                {% if page_obj.has_next %}
                                    name="page"
                                    value="{{ page_obj.next_page_number }}"
                                    type="submit"
                                {% else %}
                                    disabled="disabled"
                                {% endif %}>
                                <i class="fa fa-angle-right pagination-right"></i>
                            </button>
                        </li>
                    </ul>
                </div>

                ***
                *** Results listing
                ***
                <div class="table-inbox-wrap ">
                    <table class="table table-inbox table-hover">
                        <tbody>
                            {% for object in object_list %}
                            <tr class="{% if object.read %}read{% else %}unread{% endif %}">
                                <td class="inbox-small-cells"><input type="checkbox" class="mail-checkbox"></td>
                                <td class="inbox-small-cells"><i class="fa fa-star"></i></td>
                                <td class="view-message">
                                    <a href="{% url 'frontend:user_notification_view' object.id %}">{{ object.notification }}</a>
                                </td>
                                <td class="view-message">{{ object.notification.get_level_display }}</td>
                                <td class="view-message">{{ object.notification.type }}</td>
                                {# <td class="view-message  inbox-small-cells"><i class="fa fa-paperclip"></i></td> #}
                                <td class="view-message  text-right">{{ object.notification.date_created }}</td>
                            </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>

                ***
                *** Sample full pagination
                ***
                <ul class="pagination inbox-pagination">
                    <li>
                        <button class="np-btn"
                            {% if page_obj.has_previous %}
                                name="page"
                                value="{{ page_obj.previous_page_number }}"
                                type="submit"
                            {% else %}
                                disabled="disabled"
                            {% endif %}>
                            <i class="fa fa-angle-left  pagination-left"></i>
                        </button>
                    </li>
                    {% for page_number in paginator.page_range %}
                    <li class="{% if page_number == page_obj.number %}active{% endif %}">
                        <button class="np-btn"
                            name="page"
                            value="{{ page_number }}"
                            type="submit"
                        {% if page_number == page_obj.number %}
                            disabled="disabled"
                        {% endif %}>
                            {{ page_number }}
                        </button>
                    </li>
                    {% endfor %}
                    <li>
                        <button class="np-btn"
                            {% if page_obj.has_next %}
                                name="page"
                                value="{{ page_obj.next_page_number }}"
                                type="submit"
                            {% else %}
                                disabled="disabled"
                            {% endif %}>
                            <i class="fa fa-angle-right pagination-right"></i>
                        </button>
                    </li>
                </ul>

            </div>
        </section>
    </form>
    </div>

</div>

{% endblock content %}