Alex Nikitin
Alex Nikitin

Reputation: 941

DjangoFilterBackend filter in nested JSON filed

I have following structure:

{ 
"some_data": 123,
"social_media": {
            
                "Tiktok": "true",
                "Instagram": "true"
                  
            }
        }
 

with given list view

class PersonListView(generics.ListAPIView):
   
    serializer_class = AllPersonSerializer
    permission_class = permissions.IsAuthenticatedOrReadOnly
    filter_backends = (DjangoFilterBackend, SearchFilter)

    search_fields = ['name']

    filterset_fields = {

        'some_data': ['exact']
    }

That i basically want to do is filter my result based on json value, something like mydomain/persons/all?social_media__Tiktok=true Does DjangoFilterBackend allows it from the box or i should implement kinda custom method?

Upvotes: 2

Views: 948

Answers (2)

Rahul Raj
Rahul Raj

Reputation: 516

You can write you, filter class, for filter social_media__Tiktok and name like this way. Hope this will help you to solve your problem.

import django_filters.rest_framework
import django_filters.filters

class FilterClass(django_filters.rest_framework.FilterSet):
    filtename = filters.CharFilter(method="my_custom_filter", label="Title of filtername")

   
    class Meta:
        model = ModelName
        fields = ("filtename",'name')

    def my_custom_filter(self, queryset, name, value):
        return ModelName.objects.filter(
            Q(social_media__Tiktok=value)
           
        )

class PersonListView(ListAPIView):
    permission_classes = permission
    serializer_class = serializers
    filterset_class = FilterClass

Upvotes: 0

Daniel
Daniel

Reputation: 3527

I've implemented a custom method (or really overrid an existing method) that accomplishes what your trying to do without using django-filter.

One caveat is that here we use a ModelViewSet - so not entirely sure how this translates to a ListView. Otherwise, we will override override the get_queryset method of our ModelViewSet:

views.py

def BaseAPIView(...):

    ''' base view for other views to inherit '''

    def get_queryset(self):

        queryset = self.queryset

        # get filter request from client:
        filter_string = self.request.query_params.get('filter')

        # apply filters if they are passed in:
        if filters: 
            filter_dictionary = json.loads(filter_string)
            queryset = queryset.filter(**filter_dictionary)

        return queryset

The request url will now look like, for example: my_website.com/api/persons?filter={"social_media__Tiktok":true}

Or more precisely: my_website.com/api/persons?filter=%7B%social_media__Tiktok%22:true%7D

Which can be built like:

script.js

// using ajax as an example:
var filter = JSON.stringify({
  "social_media__Tiktok" : true
});

$.ajax({
   "url" : "my_website.com/api/persons?filter=" + filter,
   "type" : "GET",
   ...
});

Some advantages:

  • no need to specify which fields can be filtered on each view class
  • write it once, use it everywhere
  • front end filtering looks exactly like django filtering
  • can do the same with exclude

Some disadvantages:

  • potential security risks if you want some fields to be non-filterable
  • less intuitive front-end code to query a table

Overall, this approach has been far more useful for me than any packages out there.

Upvotes: 3

Related Questions