Django Filter Package – Part-1

The Django Filter is a Django Package that lets you create filters effortlessly. To use this package we must install pip install django-filter by typing into the terminal. Django Filter provides an advantage over other queryset filtering methods as it can be reused and is simple to set up less complex than other queryset filtering methods.

After we complete all 3 parts below is how our template will be rendered.

Django Filter Package Final Ourpur
Django Filter Package Final Ourpur

Table of Contents

Setup Project

If you want to know how to set up Django Project with apps than click on this link.

After you have successfully set up project goto root directory of the project and install pip install django-filter through terminal.

After installation of django-filter you must register it in settings.py file in INSTALLED_APPS.

INSTALLED_APPS = [
    ...
    'django_filters',
]

Now create an app and name it as filter_and_pagination.

And register this to INSTALLED_APPS.

    INSTALLED_APPS = [
        ...
        'django_filters',
        'filter_and_pagination.apps.FilterAndPaginationConfig',
    ]

Create urls.py file in app(filter_and_pagination) and add below code.

from django.urls import path, re_path
from filter_and_pagination import views
app_name = 'filter_and_pagination'
urlpatterns = [

]

You can see that urlpatterns has no routes we’ll create that later.

The include app(filter_and_pagination) urls.py file in projects root’s url.py file.

urlpatterns = [
    ...
    path('filter_and_pagination/', include('filter_and_pagination.urls'), name='filter_and_pagination'),
]

Creating Models and Migration

Next, we’ll create Models and Migrate them to our database. Here we’ll take the example of Books and Authors.
An Author can have multiple books and Books have a Foreign key relationship with Authors.

Creating Models

Navigate to the app in models.py file.

from django.db import models

class Book(models.Model):
    BOOK_STATUS=(
        ('PUBLISHED', 'Published'),
        ('ON_HOLD', 'On Hold'), 
    )
    id = models.AutoField(primary_key=True)
    book_name = models.CharField(max_length=255)
    author = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='author')
    status = models.CharField(max_length=255, choices = BOOK_STATUS)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table="books"
        verbose_name="Book"
        verbose_name_plural="Books"

    def __str__(self):
        return self.book_name
        
    @property
    def get_status_verbose_name(self):
        for row in range(0,2):
            if self.BOOK_STATUS[row][0] == self.status:
                status_verbose = self.BOOK_STATUS[row][1]
                break
        return status_verbose

class Author(models.Model):
    id = models.AutoField(primary_key=True)
    author_name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "authors"
        verbose_name = "Author"
        verbose_name_plural = "Authors"

    def __str__(self):
        return self.author_name

    @property
    def get_books_count(self):
        return Book.objects.filter(author=self.id).count()

We have two models Book and Author. Model Book has ForeignKey relations with Author model.
The Book models have a property called get_status_verbose_name this will return the verbose name of the book’s status.
Suppose if Book status is PUBLISHED then it will display Published for human readability.

The Author Models do not have anything special but it has get_books_count property which returns the count of Books written by the Author.
Again this does not check how many published or on hold. If you want then you can create another property or modify the existing one.

Migration

open terminal/Command Prompt from the root directory and follow the below steps.

  • python manage.py makemigrations this will create a migration file for all apps.
  • python manage.py migrate this will migrate to the database and create tables.

Getting Started with a simple example

In app(filter_and_pagination) create filter.py file. Django-Filters uses django_filters.FilterSet to generate filter results.
The result is returned as queryset and only we have to do is specify model, fields. The django_filters.FilterSet will generate form, query filtering by itself.

Let us look at a simple example in the filter.py file.

import django_filters
from filter_and_pagination.models import Book,Author

class BookFilterExp1(django_filters.FilterSet):
    book_name = django_filters.CharFilter(lookup_expr='iexact')

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

In BookFilterExp1 the filter we have two fields book_name, status.We have specified lookup_expr to book_name. The lookup_expr is used for mapping filters to models columns and field in status. we have not specified lookup_expr in this case it will consider exact as its default expression type.

Accessing in views

We’ll create a route for view.
Navigate to apps(filter_and_pagination) urls.py file.

app_name = 'filter_and_pagination'
urlpatterns = [
    path('filter_example_1', views.filter_example_1, name='filter_example_1'),
]

Create view filter_example_1 in apps(filter_and_pagination) views.py file.

def filter_example_1(request):

    books = BookFilterExp1(request.GET)
    ctx={
        "books" : books
    }
    
    path='filter_and_pagination/filter_listing.html'

    return render(request,path,ctx)

To display data create template in path=’apps(filter_and_pagination)/templates/filter_and_pagination/filter_listing.html’.
The template file must be at above mentioned else you make get an error. If you have knowledge of templates than you can place in any were.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Book</title>
</head>
<body>
    <div>
        <div style="border: 1px dashed #333;" >
            <h5 style="text-align: center;" >Book Filtering</h5>
        </div>

        <div style="" >

            <div>
                <h5>Filter Form</h5>
                <div>
                    <form action="" method="get">
                        {{books.form}}
                        <input type="submit" value="Filter">
                    </form>
                </div>
            </div>

            <table border="1" style="border-collapse: collapse">
                <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>
    </div>
</body>
</html>

We have to use books.qs for displaying books has it consists of filtered querysets.

Output

Listing authors
Listing authors

Generating filter lookups with a dictionary

We Specified book_name as CharFilter with means it uses CharField. It is not necessary to specify field type we can just pass values inside the dictionary and let Django-Filter decide which field to use for displaying in form.
Suppose we have not specified field for status but it automatically considered to display in select the tag.

class BookFilterExp1_2(django_filters.FilterSet):
    class Meta:
        model = Book
        fields = {
            'book_name' : ['icontains', 'iexact'],
            'author__author_name' : ['icontains'],
            'status' : ['exact'],
        }

The author__author_name is a ForeignKey relation for the Book and Author model. So the Author Name field will also be displayed.
We import BookFilterExp1_2 Filter to views and make new view function.

from filter_and_pagination.filter import (BookFilterExp1,BookFilterExp1_2)

def filter_example_1_2(request):
    # this is a filter example
    path='filter_and_pagination/filter_listing.html'
    books = BookFilterExp1_2(request.GET)
    ctx={
        "books" : books
    }
    return render(request,path,ctx)

Add to route urls.py file

app_name = 'filter_and_pagination'
urlpatterns = [
    ...
    path('filter_example_1_2', views.filter_example_1_2, name='filter_example_1_2'), 
]

Output

Adding HTML elements for List Filter
Adding HTML elements for List Filter

Using relationship with filter

This example is similar to that of the above once. But we have specified a filter type for author_name. This filter is of CharFilter and it uses CharField for form rendering.It also has Label which means HTML `<label></label>` Tag.
The field_name is for model field name or as relationship example : author__author_name is converted to author__author_name__icontains by Django-Filter while querying.

class BookFilterExp2(django_filters.FilterSet):

    book_name = django_filters.CharFilter(lookup_expr='iexact')
    author_name = django_filters.CharFilter(label="Author Name",field_name="author__author_name", lookup_expr='icontains')
    
    class Meta:
        model = Book
        fields = ['status']

Lets make a view for this filter

from filter_and_pagination.filter import (BookFilterExp1,BookFilterExp1_2,BookFilterExp2)

def filter_example_2(request):
    # this is a filter example
    path='filter_and_pagination/filter_listing.html'
    books = BookFilterExp2(request.GET)
    ctx={
        "books" : books
    }
    return render(request,path,ctx)

Make routes for view filter_example_2 function

app_name = 'filter_and_pagination'

urlpatterns = [
    path('listing', views.list_data, name='listing'),
    path('filter_example_1', views.filter_example_1, name='filter_example_1'),
    path('filter_example_1_2', views.filter_example_1_2, name='filter_example_1_2'),
    path('filter_example_2', views.filter_example_2, name='filter_example_2'),
]       

Result

List Filtering
List Filtering
For the official document visit this link.

Conclusion

This is the end of Part 1 of Django Filter Package – Part-1 in the next part we’ll learn about custom methods and modifying queries.

Related Posts

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