Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django-filter use paginations

I'm using the django-filter package to provide a search functionality on my List View.

Now I want to add a pagination to that view as well.
I'm trying to combine pagination to a filtered queryset, but I have no clue on how to go on.

So far, I have tried the following on views.py:

def search(request):
    qs = local_url.objects.filter(global_url__id=1).all()
    paginator = Paginator(qs, 25)
    page = request.GET.get('page')
    try:
        pub = paginator.page(page)
    except PageNotAnInteger:
        pub = paginator.page(1)
    except EmptyPage:
       pub = paginator.page(paginator.num_pages)
    url_filter = PublicationFilter(request.GET, queryset=qs)
    return render(request, 'ingester/search_list.html', {'filter': url_filter, 'publication':pub})
like image 447
Anh Tuan Nguyen Avatar asked May 18 '17 12:05

Anh Tuan Nguyen


4 Answers

This worked for me:

in my template instead of using this

<li><a href="?page={{ i }}">{{ i }}</a></li>

I wrote this:

{% if 'whatever_parameter_you_use_to_filter' in request.get_full_path %}
   <li><a href="{{ request.get_full_path }}&page={{ i }}"{{ i }}</a></li>
{% else %}
   <li><a href="?page={{ i }}">{{ i }}</a></li>
{% endif %}

I hope it helps :)

like image 135
stathoula Avatar answered Nov 10 '22 03:11

stathoula


To use Django Filter and paginate the filtered result you can do the following:

  1. Create a filter class for your model:

    On my_project/my_app/filters.py:

    import django_filters
    
    class MyModelFilter(django_filters.FilterSet):
        class Meta:
            model = MyModel
            # Declare all your model fields by which you will filter
            # your queryset here:
            fields = ['field_1', 'field_2', ...]
    
  2. Every FilterSet object has a .qs property which contains the filtered queryset and you can even override it if you want.

  3. We will paginate the .qs property of our MyModelFilter:

    On my_project/my_app/views.py:

    from . import filters
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    
    def my_view(request):
        # BTW you do not need .all() after a .filter() 
        # local_url.objects.filter(global_url__id=1) will do
        filtered_qs = filters.MyModelFilter(
                          request.GET, 
                          queryset=MyModel.objects.all()
                      ).qs
        paginator = Paginator(filtered_qs, YOUR_PAGE_SIZE)
    
        page = request.GET.get('page')
        try:
            response = paginator.page(page)
        except PageNotAnInteger:
            response = paginator.page(1)
        except EmptyPage:
            response = paginator.page(paginator.num_pages)
    
        return render(
            request, 
            'your_template.html', 
            {'response': response}
        )
    

And there you have it!


PS_1: Django filter in my experience, "plays" better with Django Rest Framework.

PS_2: If you are about to utilize DRF, I have written an example on how to use pagination in a function based view which you can easily combine with a FilterSet:

@api_view(['GET',])
def my_function_based_list_view(request):
    paginator = PageNumberPagination()
    filtered_set = filters.MyModelFilter(
                       request.GET, 
                       queryset=MyModel.objects.all()
                   ).qs
    context = paginator.paginate_queryset(filtered_set, request)
    serializer = MyModelSerializer(context, many=True)
    return paginator.get_paginated_response(serializer.data)
like image 21
John Moutafis Avatar answered Nov 10 '22 04:11

John Moutafis


To add to the answers, I did it with html tables too along with django-filters and Paginator. Below are my view and template files. The template tag is needed to make sure you pass the right parameters to the pagination url.

search_view.py

from django.shortcuts import render
from app.models.filters_model import ApiStatusFilter
from app.models.api_status import ApiStatus
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from datetime import datetime, timedelta

def status(request):
    all_entries_ordered = ApiStatus.objects.values().order_by('-created_at')[:200]

    for dictionarys in all_entries_ordered:
        dictionarys

    apistatus_list = ApiStatus.objects.values().order_by('-created_at')
    apistatus_filter = ApiStatusFilter(request.GET, queryset=apistatus_list)

    paginator = Paginator(apistatus_filter.qs, 10)
    page = request.GET.get('page')
    try:
        dataqs = paginator.page(page)
    except PageNotAnInteger:
        dataqs = paginator.page(1)
    except EmptyPage:
        dataqs = paginator.page(paginator.num_pages)

    return render(request, 'status_page_template.html', {'dictionarys': dictionarys, 'apistatus_filter': apistatus_filter, 'dataqs': dataqs, 'allobjects': apistatus_list})

status_template.html

{% load static %}
{% load my_templatetags %}

<!DOCTYPE html>
<html lang="en">
    <head>
        <link rel="stylesheet" type="text/css" href="{% static 'css/table_styling.css' %}">
        <meta charset="UTF-8">
        <title>TEST</title>
    </head>

    <body>
         <table>
            <thead>
                <tr>
                    {% for keys in dictionarys.keys %} 
                        <th>{{ keys }}</th>
                    {% endfor %}
                </tr>
            </thead>
                <form method="get">
                    {{ apistatus_filter.form.as_p }}
                    <button type="submit">Search</button>
                        {% for user in dataqs.object_list %}
                        <tr>
                            <td>{{ user.id }}</td>
                            <td>{{ user.date_time }}</td>
                            <td>{{ user.log }}</td>
                        </tr>
                        {% endfor %}
                </form>
            </tbody>
        </table>

        <div class="pagination">
            <span>
                {% if dataqs.has_previous %}
                    <a href="?{% query_transform request page=1 %}">&laquo; first</a>
                    <a href="?{% query_transform request page=dataqs.previous_page_number %}">previous</a>
                {% endif %}

                <span class="current">
                    Page {{ dataqs.number }} of {{ dataqs.paginator.num_pages }}.
                </span>

                {% if dataqs.has_next %}
                    <a href="?{% query_transform request page=dataqs.next_page_number %}">next</a>
                    <a href="?{% query_transform request page=dataqs.paginator.num_pages %}">last &raquo;</a>
                {% endif %}
            </span>
        </div> 
    </body>
</html>

my_templatetags.py

from django import template

register = template.Library()

@register.simple_tag
def query_transform(request, **kwargs):
    updated = request.GET.copy()
    for k, v in kwargs.items():
        if v is not None:
            updated[k] = v
        else:
            updated.pop(k, 0)

    return updated.urlencode()
like image 28
Elia Ahadi Avatar answered Nov 10 '22 02:11

Elia Ahadi


It took me some time to find the DRYer and cleaner solution to fix this issue and the best one, in my opinion, is the one using template tags.

from django import template

register = template.Library()

@register.simple_tag
def relative_url(value, field_name, urlencode=None):
    url = '?{}={}'.format(field_name, value)
    if urlencode:
        querystring = urlencode.split('&')
        filtered_querystring = filter(lambda p: p.split('=')[0] != field_name, querystring)
        encoded_querystring = '&'.join(filtered_querystring)
        url = '{}&{}'.format(url, encoded_querystring)
    return url

and in your template

<a href="{% relative_url i 'page' request.GET.urlencode %}">{{ i }}</a>

Source: Dealing With QueryString Parameters

like image 26
Yassine Belmamoun Avatar answered Nov 10 '22 03:11

Yassine Belmamoun