Mike Me
Mike Me

Reputation: 2392

Paginate results from django_filter

I am very new at this technology and I want to paginate a table containing results using django_filter (In my case I want to filter results from Book Model which are displayed in a HTML Table).

I tried every documentation and forum post but don't know what I am doing wrong or if I am doing something correctly.

Here is my code:

models

class Book(models.Model):

    name = models.CharField(max_length=350)
    author = models.CharField(max_length=350)
    category = models.CharField(max_length=200)

    def __str__(self):
        return self.name

filters

class BookFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_expr='icontains')
    author = django_filters.CharFilter(lookup_expr='icontains')
    category = django_filters.CharFilter(lookup_expr='icontains')

    class Meta:
        model = Book
        ields = ['name', 'author', 'category',]

views

class SearchBooksView(ListView):
    template_name = "booksearch.html"
    book_list = Book.objects.all()
    context_object_name = 'book_list'
    paginate_by = 3

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        book_filter = BookFilter(self.request.GET)
        paginator = Paginator(book_filter, self.paginate_by)
        page = self.request.GET.get('page')
            try:
                book_list = paginator.page(page)
            except PageNotAnInteger:
                book_list = paginator.page(1)
            except EmptyPage:
                book_list = paginator.page(paginator.num_pages)

        context['book_list'] = book_list

        return context

** html **

<h1 class="h1"> Search Books </h1>
<form method="get">
    {{ book_filter.form.as_p }}
    <button type="submit">Search</button>
</form>
<div class="container">
    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Author</th>
                <th>Category</th>
            </tr>
        </thead>
        <tbody>
        {% for book in book_list %}
            <tr>
                <td>{{ book.name }}</td>
                <td>{{ book.author }}</td>
                <td>{{ book.category }}</td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    {% if is_paginated %}
        <ul class="pagination">
        {% if page_obj.has_previous %}
            <li>
                <span><a href="?page={{ page_obj.previous_page_number }}">Previous</a></span>
            </li>
        {% endif %}
            <li class="">
                <span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>
            </li>
        {% if page_obj.has_next %}
            <li>
                <span><a href="?page={{ page_obj.next_page_number }}">Next</a></span>
            </li>
        {% endif %}
        </ul>
        {% else %}
            <p>No books available</p>
        {% endif %}
</div>

with this code I am getting :

paginator.py, line 84, in count return len(self.object_list)

'BookFilter' has no len()

Can you please help me? Thanks!

Upvotes: 0

Views: 275

Answers (2)

Gabriel Muj
Gabriel Muj

Reputation: 3805

You are using generic ListView from django right? That view already take care of pagination for you, you should refactor the code:

class SearchBooksView(ListView):
    model = Book
    template_name = "booksearch.html"
    context_object_name = 'book_list'
    paginate_by = 3

    def get_queryset(self):
        return BookFilter(self.request.GET, queryset=Book.objects.all()).qs

Upvotes: 0

Sagar Ramachandrappa
Sagar Ramachandrappa

Reputation: 1461

You need to pass the filtered queryset to the Paginator which can be accessed as filterObj.qs.

From the django_filter docs:

If you want to access the filtered objects in your views, for example if you want to paginate them, you can do that. They are in f.qs

So create a paginated response like this:

book_filter = BookFilter(self.request.GET)
paginator = Paginator(book_filter.qs, self.paginate_by)

Upvotes: 2

Related Questions