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

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

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.




