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

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.

#django framework #python #django #learn python

Django Filter Package - Part-3
20.35 GEEK