caravan4eg
caravan4eg

Reputation: 13

How to filter data in DRF from database by multiple keywords?

I'm a beginner and develop a little REST API project with Django rest framework. There are a bunch of records in PostgreSQL database with a text field and I have some lists of keywords. I'm trying to filter data which contain words from one or some my lists of keywords in this text field.

Can you advise me another way around to organize filtering in DRF by using a whole list of keywords at once without entering them in a form?

I'm trying to do it with django_filters

Here if filter class:

# filter

class DataFilter(django_filters.rest_framework.FilterSet):
    keyword = CharFilter(field_name='description', lookup_expr='icontains')

    class Meta:
        model = Data
        fields = ('keyword', )

Here if view class:

# view

class DataList(generics.ListAPIView): 

    def get_queryset(self):
        return Data.objects.filter(deadline__gte=date.today())

    serializer_class = DataSerializer   
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = DataFilter

But in this case, it filters only by one word which I enter in the form.

Upvotes: 1

Views: 2639

Answers (2)

Andrei Sima
Andrei Sima

Reputation: 159

In my case, I need to filter by multiple keywords and after to exclude from the filtered qs another set of keywords. Something like base_url/?kwords=kw1,kw2,kw3&exclude=e_kw1,e_kw2&others....

Inheriting from filters.BaseInFilter did not work for me... it is strange since BaseInFilter inherits from BaseCSVFilter, or I made a mistake.

My solution:

from django.db.models import Q
from django_filters import rest_framework as filters
from products.models import Product


class KwordIncludeFilter(filters.BaseCSVFilter):

    def filter(self, qs, values):
        query = Q()
        if not values:
            return qs
        else:
            for value in values:
                value = value.strip()
                query |= Q(name__icontains=value)
                qs = qs.filter(query)
            return qs


class KwordExcludeFilter(filters.BaseCSVFilter):

    def filter(self, qs, values):
        query = Q()
        if not values:
            return qs
        else:
            for value in values:
                value = value.strip()
                query |= Q(name__icontains=value)
                qs = qs.exclude(query)
            return qs


class ProductFilter(filters.FilterSet):

    kwords = KwordIncludeFilter()
    exclude = KwordExcludeFilter()

    class Meta:
        model = Product
        fields = {
            'some_other_model_filed': ['some_lookup']
        }

Upvotes: 0

ruddra
ruddra

Reputation: 51988

I think you can do it like this:

First, create a new filter set subclassing from BaseInFilter and CharFilter:

class CharInFilter(django_filters.BaseInFilter, django_filters.CharFilter):
    pass

Then, update your FilterSet class like this:

class DataFilter(django_filters.FilterSet):
    keyword__in = CharInFilter(field_name='keyword', lookup_expr='in')

    class Meta:
        model = Data
        fields = []

Then you can use this FilterSet(same as your current implementation) like this:

class DataList(generics.ListAPIView): 

    def get_queryset(self):
        return Data.objects.filter(deadline__gte=date.today())

    serializer_class = DataSerializer   
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = DataFilter

While using this filterset in DRF template, you need to input your values in comma separated format, like this: enter image description here

Upvotes: 1

Related Questions