7

I'm attempting to follow the following tutorial for pagination with django filters, but the tutorial seems to be missing something, and i'm unable to get the pagination to show using the function based views method.

https://simpleisbetterthancomplex.com/tutorial/2016/08/03/how-to-paginate-with-django.html

My updated users_list.html is the following:

   {% extends 'base.html' %}

{% load widget_tweaks %}

{% block content %}
  <form method="get">
    <div class="well">
      <h4 style="margin-top: 0">Filter</h4>
      <div class="row">
        <div class="form-group col-sm-4 col-md-4">
          <label/> 3-4 User ID
          {% render_field filter.form.employeentname class="form-control" %}
        </div>
        <div class="form-group col-sm-4 col-md-4">
          <label/> First Name
          {% render_field filter.form.employeefirstname class="form-control" %}
        </div>
        <div class="form-group col-sm-4 col-md-4">
          <label/> Last Name
          {% render_field filter.form.employeelastname class="form-control" %}
        </div>

        <div class="form-group col-sm-4 col-md-4">
          <label/> Status
          {% render_field filter.form.statusid class="form-control" %}
        </div>

        <div class="form-group col-sm-4 col-md-4">
          <label/> Title
          {% render_field filter.form.positiondesc class="form-control" %}
        </div>
      </div>
      <button type="submit" class="btn btn-primary">
        <span class="glyphicon glyphicon-search"></span> Search
      </button>
    </div>
  </form>
  <form action = "{% url 'results' %}" form method = "POST">
        {% csrf_token %}
  <table class="table table-bordered">
    {{ form.as_table }}
    <thead>
      <tr>
        <th>3-4</th>
        <th>First name</th>
        <th>Last name</th>
        <th>Title</th>
        <th>Status</th>
        <th></th>

      </tr>
    </thead>
    <tbody>
      {% for user in filter.qs %}
        <tr>
          <td>{{ user.employeentname }}</td>
          <td>{{ user.employeefirstname }}</td>
          <td>{{ user.employeelastname }}</td>
          <td>{{ user.positiondesc }}</td>
          <td>{{ user.statusid }}</td>
          <td><input type="checkbox" name="usercheck" value = "{{user.employeentname}}" />&nbsp;</td>

        </tr>
      {% empty %}
        <tr>
          <td colspan="5">No data</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>



          <button class="btn btn-primary" type="submit">Select User</button>
        </form>
{% endblock %}

My view below is based on the tutorial, but i'm using my own model:

def user_list(request):
    user_list = Employee.objects.all()
    user_filter = UserFilter(request.GET, queryset=user_list)
    user_list = user_filter.qs
    page = request.GET.get('page', 1)



    paginator = Paginator(user_list.qs, 10)
    try:
        users = paginator.page(page)
    except PageNotAnInteger:
        users = paginator.page(1)
    except EmptyPage:
        users = paginator.page(paginator.num_pages)


    args = {'filter':user_filter, 'users':users}


    return render(request, 'user_list.html', args)

My directory structure is setup as the following, perhaps its something with that:

enter image description here

from django import forms
from .models import Employee

import django_filters


class UserFilter(django_filters.FilterSet):


    class Meta:
        model = Employee
        fields =  ['employeentname', 'employeefirstname', 'employeelastname', 'statusid', 'positiondesc']





    def __init__(self, *args, **kwargs):
        super(UserFilter, self).__init__(*args, **kwargs)
        # at sturtup user doen't push Submit button, and QueryDict (in data) is empty
        if self.data == {}:
            self.queryset = self.queryset.none()

Added my base.html after moving pagination there:

{% load static %}<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <style type="text/css">
      .page-header {
        margin-top: 0;
      }
    </style>
  </head>
  <body>
    {% include 'includes/header.html' %}
    <div class="container">
      {% block content %}
      {% if users.has_other_pages %}
        <ul class="pagination">
         {% if users.has_previous %}
           <li><a href="?page={{ users.previous_page_number }}">&laquo;</a></li>
         {% else %}
          <li class="disabled"><span>&laquo;</span></li>
       {% endif %}
         {% for i in users.paginator.page_range %}
           {% if users.number == i %}
             <li class="active"><span>{{ i }} <span class="sr-only">(current)
          </span></span></li>
           {% else %}
             <li><a href="?page={{ i }}">{{ i }}</a></li>
           {% endif %}
         {% endfor %}
         {% if users.has_next %}
           <li><a href="?page={{ users.next_page_number }}">&raquo;</a></li>
         {% else %}
           <li class="disabled"><span>&raquo;</span></li>
         {% endif %}
       </ul>
         {% endif %}
      {% endblock %}
    </div>
    <script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>





    {% block javascript %}

    {% endblock %}
  </body>
</html>

What i'm expecting is for my filter to paginate the search results for every 10 users. What i'm getting now is the entire set of users. The filter works correctly, it's the pagination that isn't.

user1470034
  • 671
  • 2
  • 8
  • 23
  • Your question has `{% for user in filter.qs %}` but you don't use `filter` anywhere else in your question. – Alasdair Jan 31 '18 at 16:51
  • Isn't that calling my filter.py? I inserted the filter.py above. My search results come through correctly using filter.qs for my for loop, if i change it to users i get no results. Filter should be on filter.form, which is shown at the top of the template. – user1470034 Jan 31 '18 at 17:05
  • You can't call python modules from the template like that. Your view has a context `{ 'users': users }` so you can access `{{ users }}` in the template. The view doesn't add `filter` to the context, so `{{ filter.qs }}` isn't going to work. – Alasdair Jan 31 '18 at 17:10
  • Ok, i think I understand. I should add this to my def search(request) user_filter = UserFilter(request.GET, queryset=user_list) then call {'filter': user_filter} – user1470034 Jan 31 '18 at 17:14
  • That looks about right, it's very similar to the example in [the django-filter docs](https://django-filter.readthedocs.io/en/master/guide/usage.html#the-view). Then, once that works and you want to paginate the results, try changing the paginator to use `user_filter.qs` instead of `user_list`: `paginator = Paginator(user_filter.qs, 1)` – Alasdair Jan 31 '18 at 17:15
  • I updated my view as shown above, but still getting the same results, which are no pagination. – user1470034 Jan 31 '18 at 17:28
  • It's not clear what result you expect and what you're actually getting. You're still looping through `filter.qs`, which is the filtered, unpaginated queryset. – Alasdair Jan 31 '18 at 17:54
  • My results for the filter work correctly, but they aren't paginated. I just want a page to be added for every 10 results. – user1470034 Jan 31 '18 at 18:43
  • Then loop through the paginator’s page, instead of the filter’s queryset. – Alasdair Jan 31 '18 at 19:35
  • Which loop are you referencing? I'm a bit confused. I tried several different ways with user_list and user_filter, even page, but nothing seems to work correctly. – user1470034 Jan 31 '18 at 20:22
  • As I said you’re looping through `filter.qs` - `{% for user in filter.qs %}` - but that queryset is not paginated. Loop through the page instead `{% for user in users %}`. – Alasdair Jan 31 '18 at 20:43
  • If i change it to user in users then my filter doesn't work. – user1470034 Jan 31 '18 at 20:52
  • Your current edit `paginator = Paginator(user_list.qs, 10)` doesn't make sense. The queryset `user_list` doesn't have an attribute `qs`. You should use the filter's queryset - `paginator = Paginator(user_filter.qs, 10)`. There's a couple of answers on [this question](https://stackoverflow.com/questions/44048156/django-filter-use-paginations) that suggest that approach, so it should work. If you can't get that working, I'm afraid I don't have any other suggestions. – Alasdair Feb 01 '18 at 09:53

6 Answers6

10

You can paginate like this:

Note: user_filter.qs has filtered results and user_filter.queryset has unfiltered results

views.py

      def search(request):
         user_list = Employee.objects.all()
         user_filter = UserFilter(request.GET, queryset=user_list)
         user_list = user_filter.qs
         
         paginator = Paginator(user_list, 10)
         page = request.GET.get('page', 1)
         try:
            users = paginator.page(page)
         except PageNotAnInteger:
            users = paginator.page(1)
         except EmptyPage:
            users = paginator.page(paginator.num_pages)
         args = {'paginator': paginator,'filter':user_filter, 
           'users':users,}
         return render(request, 'search/user_list.html', args)

And then in the template:

   {% for user in users %}
     <tr>
      <td>{{ user.employeeusername }}</td>
      <td>{{ user.employeefirstname }}</td>
      <td>{{ user.employeelastname }}</td>
      <td>{{ user.statusid }}</td>
      <td><input type="checkbox" name="usercheck" />&nbsp;</td>

    </tr>
  {% empty %}
    <tr>
      <td colspan="5">No data</td>
    </tr>
  {% endfor %}
</tbody>
 {% if users.has_other_pages %}
   <ul class="pagination">
    {% if users.has_previous %}
      <li><a href="?page={{ users.previous_page_number }}">&laquo;</a></li>
    {% else %}
     <li class="disabled"><span>&laquo;</span></li>
  {% endif %}
    {% for i in users.paginator.page_range %}
      {% if users.number == i %}
        <li class="active"><span>{{ i }} <span class="sr-only">(current)
     </span></span></li>
      {% else %}
        <li><a href="?page={{ i }}">{{ i }}</a></li>
      {% endif %}
    {% endfor %}
    {% if users.has_next %}
      <li><a href="?page={{ users.next_page_number }}">&raquo;</a></li>
    {% else %}
      <li class="disabled"><span>&raquo;</span></li>
    {% endif %}
  </ul>
{% endif %}
Rahul Hindocha
  • 170
  • 2
  • 14
Jass Waraich
  • 346
  • 2
  • 10
  • I've updated my code, but my pagination still isn't working. I've changed my code to match yours, i've also included my filters.py and directory structure, perhaps it's something with that. It's not working on the search results of user_list. – user1470034 Mar 05 '18 at 16:51
  • 1
    Trying moving pagination to the base.html and have you changed filter.qs to users? – Jass Waraich Mar 05 '18 at 18:10
  • I updated my code once again. Using the base.html now. However, if I change {% for user in filter.qs %} to {% for user in users %} my filter stops functioning. – user1470034 Mar 05 '18 at 20:24
1

I have tested it right now!It works fine for me.Just sharing a portion of the template with a view like the previous answer

      <div class="row">
        <div class="form-group col-sm-4 col-md-3">

          {{ filter.form.row_date.label_tag }}
          {% render_field filter.form.row_date class="form-control" %}

        </div>

        <div class="form-group col-sm-4 col-md-3">
          {{ filter.form.director.label_tag }}
          {% render_field filter.form.director class="form-control" %}
        </div>
        <div class="form-group col-sm-8 col-md-6">
          {{ filter.form.manager.label_tag }}
          {% render_field filter.form.manager class="form-control" %}
        </div> 
        <div class="form-group col-sm-8 col-md-6">
          {{ filter.form.analyst.label_tag }}
          {% render_field filter.form.analyst class="form-control" %}
        </div> 
        <div class="form-group col-sm-8 col-md-6">
        <button type="submit" class="btn btn-primary">
        <span class="glyphicon glyphicon-search"></span> Search
        </button>
        </div>
        </div>
      </div>

    </div>
  </form>

  <table class="table table-bordered" >
    <thead>
      <tr>

        <th>row_date</th>
        <th>Director</th>
        <th>Manager</th>
        <th>Analyst</th>

      </tr>
    </thead>
    <tbody>
      {% for a in users %}
        <tr>
          <td>{{ a.row_date }}</td>
          <td>{{ a.director }}</td>
          <td>{{ a.manager }}</td>
          <td>{{ a.analyst }}</td>

          <td colspan="5">No data</td>
        </tr>
        {% empty %}
        <tr>
          <td colspan="5">No data</td>
        </tr>
        {% endfor %}
      </tbody>
      </table>
    </div>
       {% block javascript %}
    <script src="{% static 'search/js/jquery-3.1.1.min.js' %}"></script>
    <script src="{% static 'search/js/bootstrap.min.js' %}"></script>

     {% endblock %}
  </body>
</html>
Jass Waraich
  • 346
  • 2
  • 10
-1

It's working fine for me. And if still, it's not working out for you, It's better to save time and try other alternatives.

Here are some suggestions:

1.Please try Q objects

2.You can do a lot with kwargs

3.Django REST Framework Filtering

4.For search-engine like capability haystack is a beast - maybe a bit complicated for a newbie.

5.You can also use Elastic Search.

Views.py

def avail_list(request):
        avails1 = AvailstaticCopy.objects.all().order_by('-row_date')
        avail_filter = AvailFilter(request.GET, queryset=avails1)
        avails1 = avail_filter.qs


        paginator = Paginator(avails1, 144)
        page = request.GET.get('page',1)

        try:
            users = paginator.page(page)
        except PageNotAnInteger:
            users = paginator.page(1)
        except EmptyPage:
            users = paginator.page(paginator.num_pages)


        context = {
            'paginator': paginator,
            'users': users,
            'filter': avail_filter,
        }
        return render(request,'avails/avail_list.html',context)

A Portion of the template for reference with a situation similar to the question:

     <div class="row">
        <div class="form-group col-sm-4 col-md-3">

          {{ filter.form.row_date.label_tag }}
          {% render_field filter.form.row_date class="form-control" %}

        </div>

        <div class="form-group col-sm-4 col-md-3">
          {{ filter.form.director.label_tag }}
          {% render_field filter.form.director class="form-control" %}
        </div>
        <div class="form-group col-sm-8 col-md-6">
          {{ filter.form.manager.label_tag }}
          {% render_field filter.form.manager class="form-control" %}
        </div> 
        <div class="form-group col-sm-8 col-md-6">
          {{ filter.form.analyst.label_tag }}
          {% render_field filter.form.analyst class="form-control" %}
        </div> 
        <div class="form-group col-sm-8 col-md-6">
        <button type="submit" class="btn btn-primary">
        <span class="glyphicon glyphicon-search"></span> Search
        </button>
        </div>
        </div>
      </div>

    </div>


  <table class="table table-bordered" >
    <thead>
      <tr>

        <th>row_date</th>
        <th>Director</th>
        <th>Manager</th>
        <th>Analyst</th>

      </tr>
    </thead>
    <tbody>
      {% for a in users %}
        <tr>
          <td>{{ a.row_date }}</td>
          <td>{{ a.director }}</td>
          <td>{{ a.manager }}</td>
          <td>{{ a.analyst }}</td>
        </tr>
        {% empty %}
        <tr>
          <td colspan="5">No data</td>
        </tr>
        {% endfor %}
      </tbody>
      </table>
    </div>
       {% block javascript %}
    <script src="{% static 'search/js/jquery-3.1.1.min.js' %}"></script>
    <script src="{% static 'search/js/bootstrap.min.js' %}"></script>

     {% endblock %}
  </body>
</html>
Jass Waraich
  • 346
  • 2
  • 10
  • Any chance there is something i need to include in my settings.py for pagination that may be missing? The pagination never occurs, so it's not giving an error, but could that be it? – user1470034 Mar 07 '18 at 14:39
  • As you're using Django widget tweaks so I believe you have already installed widget-tweaks with `pip install django-widget-tweaks` and included in the `INSTALLED_APPS = [ 'widget_tweaks', ]` Only other things are that Django can find your static files and templates, and you're filters.py is correctly setup – Jass Waraich Mar 07 '18 at 14:55
  • What does your url look like for avails/avail_list.html? I'm using return render(request, 'search/search.html', context) and my project url is url(r'^search/$', FilterView.as_view(filterset_class=UserFilter, template_name='search/search.html'), name='search'), with url(r'^search/', include('mysite.search.urls')), – user1470034 Mar 07 '18 at 16:15
  • Here is the url for the app for the avail_list which is my search view `url(r'^account/$', views.avail_list, name='avail_list'),` and for the project `urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^', include('home2.urls')), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)` – Jass Waraich Mar 07 '18 at 16:38
-1

I don't know if this is relevant but it seems to me that the issue may be where you are setting the paginator. In your view you have

paginator = Paginator(user_list.qs, 10)

but if you look closely at the solutions provided they are suggesting that you should be using

paginator = Paginator(user_list, 10) 

ie without .qs as you've already defined user_list in a previous line with user_list = user_filter.qs

cander
  • 279
  • 6
  • 14
-1

Implementation Step

  1. install package by pip install filter-and-pagination
  2. import FilterPagination by from filter_and_pagination import FilterPagination in view.py
  3. in your function writte code as bellow standards...
queryset = FilterPagination.filter_and_pagination(request, Customer)
serialize_data = CustomerSerializer(queryset['queryset'], many=True).data
resultset = {'dataset': serialize_data, 'pagination': queryset['pagination']}
  • in this code Customer is Django model &
  • CustomerSerializer is a DRF Serializer class
  1. in the resultset it contains dataset & pagination data, In this format (API Response) link: https://github.com/ashish1997it/filter-pagination-dj#demo
  2. For the API request follow PostMan collection link: https://github.com/ashish1997it/filter-pagination-dj#postman in the header section it will take a parameter & request you customize as per your requirement

If you still face any difficulty then contact me :)

Ashish Sondagar
  • 917
  • 1
  • 9
  • 16
-2

I have written a post on implementing pagination with Django with function based view on my blog, you can check here

I have discussed several ways to implement pagination in Django

Jai Singhal
  • 410
  • 5
  • 9
  • How would this work with Django Filter? I attempted to implement this and it worked on the initial load but broke my filter. I can't change this line {% for user in filter.qs %} to {% for obj in object_list %} if i do it breaks. – user1470034 Feb 01 '18 at 15:47