Django Filter Package – Part-3

In our previous post on Django-Filters Part 2, we learned adding custom methods to filter field, Implementing widgets and modifying form attributes, and finally to Paginate. And in our final post, we’ll learn to use multiple choice fields and finally format with CSS bootstrap.

Table of Contents

Filter Data using Multiple Choice Field

class BookFilterExp7(django_filters.FilterSet):

    BOOKS_SORT_CHOICES = (
        ('LESS_THAN', 'Less Than'),
        ('GREATER_THAN', 'Greater Than'),
    )

    BOOKS_ORDER_BY_CHOICES = (
        ('ASC', 'Ascending Order'),
        ('DESC', 'Descending Order'),
    )
    
    book_name = django_filters.CharFilter(lookup_expr='iexact')
    
    author_name = django_filters.CharFilter(label="Author Name",field_name="author__author_name",lookup_expr='icontains',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder' : "Search by Author..."}))
    
    author_books = django_filters.NumberFilter(label="Number of Books Published By Author",method="author_books_count")
    
    author_sort_books_by = django_filters.ChoiceFilter(empty_label='Select to Sort',label="Sort Number of Books",choices=BOOKS_SORT_CHOICES,method="author_sort_books")
    
    books_by_order = django_filters.ChoiceFilter(empty_label='Select to Order',label="Order By",choices=BOOKS_ORDER_BY_CHOICES,method="books_by_order_method")

    num_rows = django_filters.NumberFilter(label="Number of Rows to show",method="num_rows_method",widget=forms.TextInput(attrs={'min':'1', 'required' : True}))

    multiple_authors = django_filters.MultipleChoiceFilter(choices=[],label="Filter by multiple Authors",method="multiple_authors_method",widget=forms.SelectMultiple(attrs={"class" : "form-control"}))

    class Meta:
        model = Book
        fields = ['status']

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.form.fields['book_name'].widget.attrs = {'placeholder':'Type Book to search...'} 
        self.form.fields['multiple_authors'].choices = self.get_multiple_authors_choices()
        self.request_data = args[0].dict()
        
    def author_books_count(self, queryset, name, value):
        sort_by = self.request_data.get('author_sort_books_by',None)
        authors_qs = Author.objects.all()
        authors_id = []
        for author in authors_qs:
            count = Book.objects.filter(author=author).count()
            if sort_by==self.BOOKS_SORT_CHOICES[1][0]: #greater than
                if count >= value:
                    authors_id.append(author.id)
            else:
                if count <= value:
                    authors_id.append(author.id)

        qs = Book.objects.filter(author__in=authors_id)
        return qs

    def author_sort_books(self, queryset, name, value):
        return queryset

    def books_by_order_method(self, queryset, name, value):
        order_by = "-book_name"
        if value == self.BOOKS_ORDER_BY_CHOICES[0][0]:
            order_by = "book_name"
        return queryset.order_by(order_by)

    @property
    def qs(self):
        qs = super().qs
        num_rows = int(self.request_data.get('num_rows',5))
        if num_rows <= 0:
            num_rows = qs.count()
        paginator = Paginator(qs,num_rows)
        page = self.request_data.get('page')
        paginated_qs = paginator.get_page(page)
        return paginated_qs

    def num_rows_method(self, queryset, name, value):
        return queryset

    def get_multiple_authors_choices(self):
        choices = []
        for author in Author.objects.all():
            choices.append([author.id, author.author_name])
        return choices

    def multiple_authors_method(self, queryset, name, value):
        ids = []
        for item in value:
            if item != "":
                ids.append(item)
        if len(ids):
            qs = queryset.filter(author__in=ids)
        else:
            qs = queryset
        return qs

The multiple_authors is MultipleChoiceFilter and to make it select multiple authors we added widget=forms.SelectMultiple(attrs={"class" : "form-control"}).
The argument choices is passed empty and on __init__ the method we’ll pass dynamically generated data from a method self.get_multiple_authors_choices().
In this method, we loop through all authors and add them to choices which is a list and returned to multiple_authors a field as choices.

Make views, routes, and template

def filter_example_7(request):
    books = BookFilterExp7(request.GET)
    ctx={
        "books" : books,
        "filter_url" : urlencode(request.GET.dict()),
    }
    path='filter_and_pagination/filter_listing_example_7.html'
    return render(request,path,ctx)

Make a new Template in path=”filter_and_pagination/templates/filter_and_pagination/filter_listing_example_7.html”.

    <div class="container" >
    <div class="panel panel-primary">
        <div class="panel-heading">Welcome to Book Store</div>
        <div class="panel-body"><strong>Books Listing</strong></div>
    </div>
    
    <div>
        <h3>Filter Books</h3>
    </div>
    <div>
        <form action="" method="get">
            <div class="row" style="display: block" >
                <div class="col-md-2">
                    <div>{{books.form.book_name.label}}</div>
                    <div>{{books.form.book_name}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.author_name.label}}</div>
                    <div>{{books.form.author_name}}</div>
                </div>

                <div class="col-md-3">
                    <div>{{books.form.author_books.label}}</div>
                    <div>{{books.form.author_books}}</div>
                </div>

                <div class="col-md-3">
                    <div>{{books.form.multiple_authors.label}}</div>
                    <div>{{books.form.multiple_authors}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.author_sort_books_by.label}}</div>
                    <div>{{books.form.author_sort_books_by}}</div>
                </div>

            </div>

            <div class="row" style="display: block" >
                <div class="col-md-2">
                    <div>{{books.form.books_by_order.label}}</div>
                    <div>{{books.form.books_by_order}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.num_rows.label}}</div>
                    <div>{{books.form.num_rows}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.status.label}}</div>
                    <div>{{books.form.status}}</div>
                </div>

                <div class="col-md-2" >
                    <input type="submit" style="margin-top:15px;" class="btn btn-primary btn-md" value="Filter">
                </div>

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

    <table class="table table-hover">
        <thead>
            <tr>
                <th>Sl no</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Book Status</th>
                <th>Total Books Count</th>
            </tr>
        </thead>

        <tbody> 
            {% for book in books.qs %}
            <tr>
                <td>{{forloop.counter}}</td>
                <td>{{book.book_name}}</td>
                <td>({{book.author.id}}) <strong>{{book.author.author_name}}</strong></td>
                <td>{{book.get_status_verbose_name}}</td>
                <td>{{book.author.get_books_count}}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>

    <div class="" >
        <h5>Pagination Example 1                                                                            </h5>
        <ul class="pagination">
            {% if books.qs.has_previous %}
                <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.previous_page_number}}"><span class="glyphicon glyphicon-chevron-left"></span></a></li>
            {% endif %}

            {% for num in books.qs.paginator.page_range %}
                {% if books.qs.number == num %}
                    <li class="active"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{num}}">{{num}}</a></li>
                {% elif num > books.qs.number|add:'-3' and num < books.qs.number|add:'3' %}
                    <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?{{filter_url}}&page={{num}}">{{num}}</a></li>
                {% endif %}
            {% endfor %}

            {% if books.qs.has_next %}
                <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.next_page_number}}"><span class="glyphicon glyphicon-chevron-right"></span></a></li>
            {% endif %}
            
        </ul>

        <h5>Pagination Example 2</h5>
        <div style="width: 250px;" >
            <ul class="pager">
                {% if books.qs.has_previous %}
                    <li class="previous"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.previous_page_number}}"><span class="glyphicon glyphicon-chevron-left"></span> Previous</a></li>
                {% endif %}
                
                {% if books.qs.has_next %}
                    <li class="next"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.next_page_number}}">Next<span class="glyphicon glyphicon-chevron-right"></span></a></li>
                {% endif %}
            </ul>
        </div>
    </div>
</div>

Output

Addding Pagination to Filters
Adding Pagination to Filters

Formatting form with bootstrap

So we have completed the functionality of the Booking Filter its time to format it in a nice way such a way that it is presentable.
We are just going to make changes in our template and our filters, views and urls remains the same.

Make a new Template in path=”filter_and_pagination/templates/filter_and_pagination/filter_listing_example_8.html”.

    <div class="container" >
    <div class="panel panel-primary">
        <div class="panel-heading">Welcome to Book Store</div>
        <div class="panel-body"><strong>Books Listing</strong></div>
    </div>
    
    <div>
        <h3>Filter Books</h3>
    </div>
    <div>
        <form action="" method="get">
            <div class="row" style="display: block" >
                <div class="col-md-2">
                    <div>{{books.form.book_name.label}}</div>
                    <div>{{books.form.book_name}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.author_name.label}}</div>
                    <div>{{books.form.author_name}}</div>
                </div>

                <div class="col-md-3">
                    <div>{{books.form.author_books.label}}</div>
                    <div>{{books.form.author_books}}</div>
                </div>

                <div class="col-md-3">
                    <div>{{books.form.multiple_authors.label}}</div>
                    <div>{{books.form.multiple_authors}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.author_sort_books_by.label}}</div>
                    <div>{{books.form.author_sort_books_by}}</div>
                </div>

            </div>

            <div class="row" style="display: block" >
                <div class="col-md-2">
                    <div>{{books.form.books_by_order.label}}</div>
                    <div>{{books.form.books_by_order}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.num_rows.label}}</div>
                    <div>{{books.form.num_rows}}</div>
                </div>

                <div class="col-md-2">
                    <div>{{books.form.status.label}}</div>
                    <div>{{books.form.status}}</div>
                </div>

                <div class="col-md-2" >
                    <input type="submit" style="margin-top:15px;" class="btn btn-primary btn-md" value="Filter">
                </div>

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

    <table class="table table-hover">
        <thead>
            <tr>
                <th>Sl no</th>
                <th>Book Name</th>
                <th>Author</th>
                <th>Book Status</th>
                <th>Total Books Count</th>
            </tr>
        </thead>

        <tbody> 
            {% for book in books.qs %}
            <tr>
                <td>{{forloop.counter}}</td>
                <td>{{book.book_name}}</td>
                <td>({{book.author.id}}) <strong>{{book.author.author_name}}</strong></td>
                <td>{{book.get_status_verbose_name}}</td>
                <td>{{book.author.get_books_count}}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>

    <div class="" >
        <h5>Pagination Example 1</h5>
        <ul class="pagination">
            {% if books.qs.has_previous %}
                <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.previous_page_number}}"><span class="glyphicon glyphicon-chevron-left"></span></a></li>
            {% endif %}

            {% for num in books.qs.paginator.page_range %}
                {% if books.qs.number == num %}
                    <li class="active"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{num}}">{{num}}</a></li>
                {% elif num > books.qs.number|add:'-3' and num < books.qs.number|add:'3' %}
                    <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?{{filter_url}}&page={{num}}">{{num}}</a></li>
                {% endif %}
            {% endfor %}

            {% if books.qs.has_next %}
                <li><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.next_page_number}}"><span class="glyphicon glyphicon-chevron-right"></span></a></li>
            {% endif %}
            
        </ul>

        <h5>Pagination Example 2</h5>
        <div style="width: 250px;" >
            <ul class="pager">
                {% if books.qs.has_previous %}
                    <li class="previous"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.previous_page_number}}"><span class="glyphicon glyphicon-chevron-left"></span> Previous</a></li>
                {% endif %}
                
                {% if books.qs.has_next %}
                    <li class="next"><a href="{% url 'filter_and_pagination:filter_example_8' %}?page={{books.qs.next_page_number}}">Next<span class="glyphicon glyphicon-chevron-right"></span></a></li>
                {% endif %}
            </ul>
        </div>
    </div>
</div>

Output

Final output of Django Filters
The final output of Django Filters

Conclusion

This is the end of Part 3 of the Django Filter Package.if you have doubts then comment below.

Summary
Review Date
Reviewed Item
Django Filter Package - Part-3
Author Rating
51star1star1star1star1star
Software Name
Django Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development