Фильтр DjangoFilterBackend во вложенном файле JSON

#django #django-rest-framework #django-filter

Вопрос:

У меня есть следующая структура:

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

с заданным представлением списка

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

    search_fields = ['name']

    filterset_fields = {

        'some_data': ['exact']
    }
 

Что я в основном хочу сделать, это отфильтровать свой результат на основе значения json, что-то вроде mydomain/persons/all?social_media__Tiktok=true
Позволяет ли DjangoFilterBackend это из коробки, или я должен реализовать какой-то пользовательский метод?

Ответ №1:

Я реализовал пользовательский метод (или действительно переопределил существующий метод), который выполняет то, что вы пытаетесь сделать, не используя django-filter .

Одно предостережение состоит в том, что здесь мы используем a ModelViewSet — поэтому не совсем уверены, как это переводится в a ListView . В противном случае мы переопределим переопределение get_queryset метода нашего 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

 

URL-адрес запроса теперь будет выглядеть, например: my_website.com/api/persons?filter={"social_media__Tiktok":true}

Или точнее: my_website.com/api/persons?filter={%social_media__Tiktok":true}

Которые могут быть построены как:

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",
   ...
});
 

Некоторые преимущества:

  • нет необходимости указывать, какие поля могут быть отфильтрованы для каждого класса представления
  • напишите это один раз, используйте везде
  • фильтрация переднего плана выглядит точно так же, как фильтрация django
  • можете сделать то же самое с exclude

Некоторые недостатки:

  • потенциальные риски для безопасности, если вы хотите, чтобы некоторые поля не фильтровались
  • менее интуитивно понятный интерфейсный код для запроса таблицы

В целом, этот подход оказался для меня гораздо более полезным, чем любые существующие пакеты.

Ответ №2:

Вы можете написать себя, класс фильтра, для фильтра social_media__Tiktok и name так далее. Надеюсь, это поможет вам решить вашу проблему.

 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