Фасеттинг с помощью Django Rest Framwork / Django Filter

Дорогие товарищи,

В настоящее время я создаю API на основе Django Rest Framework с Django Filter.

Поиск, фильтрация и сортировка работают достаточно хорошо, хотя мне было интересно узнать о фасетах фильтра.

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

Какой хороший способ предоставить список вариантов выбора (фасет) для каждого фильтра? Имеет ли смысл использовать другую конечную точку, которая предоставляет только выбор? На мой взгляд, было бы правильно объявлять фасеты внутри подкласса FilterSet, потому что именно в нем происходит все, что связано с фильтрами

Ждем ваших комментариев!

Юлиан

PS: Обычно я использую ElasticSearch в подобных ситуациях, но мне интересно, есть ли идиоматическое решение в обычном DRF/DF.

# models.py

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=200)


# filters
from django_filters import rest_framework as filters
from django.db.models.functions import Substr


def get_first_letter_choices():
    # Create choices
    letters = Person.objects \
        .all() \
        .annotate(first_letter=Substr('name', 1, 1)) \
        .values_list("first_letter", flat=True) \
        .distinct()
        
    return [(letter, letter, ) for letter in letters]

class PersonFilterSet(filters.FilterSet):
    first_letter = filters.ChoiceFilter(
        choices=get_first_letter_choices,
        field_name="name",
        lookup_expr="startswith"
    )

# DRF Views/Serializers
# serializers.py
from rest_framework import serializers

class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ["name", ]


# views.py
from rest_framework import ListAPIView

class PersonsIndex(ListAPIView):
    filterset_class = PersonFilterSet
    serializer_class = PersonSerializer

    def get_queryset(self):
        return Person.objects.all()

Я пробовал использовать другую конечную точку и использовать пользовательский сериализатор, который будет включать поля выбора, но это похоже на борьбу с DRF/DF.

Вернуться на верх