Search code examples
djangosearchfiltermodeldjango-filter

Django-filter ForeignKey related Model filtering


I've been struggling with dajngo-filter recently. I have 2 models: Book and Author.

class Book(models.Model):
   title = models.CharField(max_length=150)
   author = models.ForeignKey(Author, on_delete=model.CASCADE)
   length = models.PositiveIntegerField()

class Author(models.Model):
    first_name = models.CharField(max_length=150)
    last_name = models.CharField(max_length=150)
    year_of_birth = models.DateFiled()

So, I'd like to make filter page, where you can find book by it's properties (title, author etc) AND information about author. For example book shorter than 300 pages, which author is beetween 20-25 years old.

In filters.py I have:

import django_filters
from .models import Book, Author

class BookFilter(django_filters.FilterSet):
    class Meta():
        model = Book
        fields = ['title', 'author']

And have actually no idea how to also filter by different model, which is related by ForeignKey to Book model.

I have really little experience with Dajngo, I wonder if it's very complex problem or not. :) Thanks!


Solution

  • Basically there are two important arguments for each filter:

    field_name: The name of the model field to filter on. You can traverse “relationship paths” using Django’s __ syntax to filter fields on a related model. ex, author__last_name.

    lookup_expr: The field lookup to use when filtering. Django’s __ syntax can again be used in order to support lookup transforms. ex, year__gte. For full list of field lookups you can use see here: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#field-lookups.

    Together, the field field_name and lookup_expr represent a complete Django lookup expression. A detailed explanation of lookup expressions is provided in Django’s lookup reference here: https://docs.djangoproject.com/en/2.2/ref/models/lookups/#module-django.db.models.lookups.

    With that in mind here is the answer:

    import django_filters
    from .models import Book, Author
    
    class BookFilter(django_filters.FilterSet):
        title = django_filters.CharFilter(field_name='title', lookup_expr='iexact')
        author = django_filters.CharFilter(field_name='author__last_name', lookup_expr='iexact')
        
        class Meta():
            model = Book
    

    If you want to filter by exact date just add to your BookFilter class:

    date_of_birth = django_filters.DateFilter(field_name='author__date_of_birth', lookup_expr='exact')
    

    However it would make more sense to filter by year of birth (for example to get all the books from authors born between 1980 and 1990). To do that here is what you add to BookFilter class:

    birth_year__gt = django_filters.NumberFilter(field_name='author__date_of_birth', lookup_expr='year__gt')
    birth_year__lt = django_filters.NumberFilter(field_name='author__date_of_birth', lookup_expr='year__lt')