Baldie47
Baldie47

Reputation: 1264

FIlter Class Based View ListView that has pagination

I'm trying to do a ListView from a Class Based View that has filtering, and pagination

I was able to get the table showing and the pagination working as I want, this gives me a basic list of all invoices that I need to show:

enter image description here

but I'm not able to make the filtering work, for what I understand I need to override the "get_queryset" method and this would return a queryset, (in this cased called "invoices_filtered_list") however I don't know how should I render this in the .html page so it will have the textbox in which I should type.

These are my files:

views.py

from django.views.generic import (TemplateView, ListView)
from .models import WoodhistAzolveInvoices
from .filters import WoodhistAzolveInvoicesFilter


class InvoicesListView(ListView):
    model = WoodhistAzolveInvoices
    template_name = 'home.html'
    context_object_name = 'invoices'
    ordering = ['suppliername', 'invoicenumber']
    paginate_by = 50

    def get_queryset(self):
        qs = self.model.objects.all()
        invoices_filtered_list = WoodhistAzolveInvoicesFilter(self.request.GET, queryset=qs)
        return invoices_filtered_list.qs

urls.py

from django.urls import path
from .views import InvoicesListView

app_name = 'web'

urlpatterns = [
    path('', InvoicesListView.as_view(), name='home'),
]

filters.py

import django_filters
from .models import *

class WoodhistAzolveInvoicesFilter(django_filters.FilterSet):
    class Meta:
        model = WoodhistAzolveInvoices
        fields = ['suppliername']

models.py

from django.db import models


class WoodhistAzolveInvoices(models.Model):
    docid = models.CharField(db_column='DocId', primary_key=True, max_length=50)  # Field name made lowercase.
    supplierreference = models.CharField(db_column='SupplierReference', max_length=50, blank=True, null=True)  # Field name made lowercase.
    suppliername = models.CharField(db_column='SupplierName', max_length=100, blank=True, null=True)  # Field name made lowercase.
    invoicenumber = models.CharField(db_column='InvoiceNumber', max_length=50, blank=True, null=True)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'WoodHist_Azolve_Invoices'

home.html

{% extends "base.html" %}

{% block title %} base title {% endblock title %}


{% block content %}
<h1> azolve home </h1>
<div class="row">
  <div class="col">
    <div class="card card-body">
      <form method="get">
        {{ invoices_filtered_list.form }}
        <button class="btn btn-primary" type="submit">Search</button>
      </form>

    </div>
  </div>
</div>


<table id="myTable1" class="table table-striped table-bordered" style="width:100%">
  <thead>
  <tr>
    <th scope="col">Supplier Name</th>
    <th scope="col">Invoice Number</th>
    <th scope="col">Reference</th>
  </tr>
  </thead>
  <tbody>
  {% for datarow in invoices %}
  <tr>
    <td>{{datarow.suppliername}}</td>
    <td>{{datarow.invoicenumber}}</td>
    <td>{{datarow.supplierreference}}</td>
  </tr>
  {% endfor %}
  {% if is_paginated %}
    {% if page_obj.has_previous %}
      <a class="btn btn-outline-info" href="?page=1">First</a>
      <a class="btn btn-outline-info" href="?page={{ page_obj.previous_page_number}}">Previous</a>
    {% endif %}

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

    {% if page_obj.has_next %}
    <a class="btn btn-outline-info" href="?page={{ page_obj.next_page_number }}">Next</a>
    <a class="btn btn-outline-info" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
    {% endif %}

  {% endif %}
  </tbody>
</table>
{% endblock content %}

If it worth noting, I'm using the package "django_filters" and Django 3.2.4

Upvotes: 1

Views: 1029

Answers (1)

Mojtaba Arezoomand
Mojtaba Arezoomand

Reputation: 2380

You want filtering and pagination at the same time.

you need to make your pagination to keep your querystrings then you can have them at the same time. Because when you click on next page your querystring will be like this .../?page=page_number so your filtering will be gone.

you can do this:

  1. Inside your app directory create a folder called templatetags.

  2. Inside the templatetags folder create a file called pagination_tags.py(name is up to you).

    # Your template tag in app_name/templatetags/pagination_tags.py
    from django import template
    from urllib.parse import urlencode
    
    
    register = template.Library()
    
    @register.simple_tag
    def url_replace (request, field, value):
        dict_ = request.GET.copy()
        dict_[field] = value
    
        return dict_.urlencode()   
    
  3. After creating your template tag load it in your html page and use it.

    {% load pagination_tags %}
    
    {% if is_paginated %}
    <nav aria-label="Page navigation example" class="d-flex justify-content-center pt-3">
      <ul class="pagination">
        {% if page_obj.has_previous and page_obj.number != 2 %}
        <li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' 1 %}" tabindex="-1" aria-disabled="true">First Page</a></li>
        {% endif %}
        {% if page_obj.has_previous %}
        <li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.previous_page_number %}">{{ page_obj.previous_page_number }}</a></li>
        {% endif %}
        <li class="page-item disabled"><a class="page-link" href="#">{{ page_obj.number }}</a></li>
        {% if page_obj.has_next %}
        <li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.next_page_number %}">{{ page_obj.next_page_number }}</a></li>
        {% endif %}
        {% if page_obj.paginator.num_pages != page_obj.number and page_obj.paginator.num_pages != page_obj.next_page_number %}
        <li class="page-item"><a class="page-link text-dark" href="?{% url_replace request 'page' page_obj.paginator.num_pages %}">Last Page</a></li>
        {% endif %}
      </ul>
    </nav>
    {% endif %}  
    
  4. And your ListView should be replaced by FilterView

    from django_filters.views import FilterView
    from .models import WoodhistAzolveInvoices
    from .filters import WoodhistAzolveInvoicesFilter
    
    
    class InvoicesListView(FilterView):
        model = WoodhistAzolveInvoices
        template_name = "home.html"
        filterset_class = WoodhistAzolveInvoicesFilter
        context_object_name = 'invoices'
        ordering = ['suppliername', 'invoicenumber']
        paginate_by = 50 
    

Upvotes: 1

Related Questions