#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