Как отфильтровать внешний ключ с помощью ModelMultipleChoiceFilter в DRF?
У меня возникают проблемы с использованием ModelMultipleChoiceFilter в DRF.
Во-первых, у меня есть три модели.
# JUST STRUCTURE
class A -> fields : [user:User]
class B -> fields : [a:A(related_name="b"), c:C]
class C -> fields : [name:string]
И я хочу отфильтровать имена C (множественный выбор) в классе A.
Так... в views.py
,
class ShortFormListCreateView(generics.ListCreateAPIView):
queryset = A.objects.all()
filter_backends = [DjangoFilterBackend]
filterset_class = AFilterSet
и в filters.py
,
class AFilterSet(FilterSet):
aa = filters.ModelMultipleChoiceFilter(
field_name='b__c__name',
to_field_name='name',
lookup_expr='in',
queryset=C.objects.all(),
method='filter_cc',
widget=CSVWidget,
)
class Meta:
model = ShortForm
fields = ['aa']
def filter_cc(self, queryset, name, value):
c_ids = [v.id for v in value]
if value:
queryset = queryset.filter(b__c__in=c_ids).distinct()
return queryset
Это сработало, но я думаю, что это не эффективный способ. Есть ли какие-нибудь решения?
Оптимизация оценки кверисетов: Убедитесь, что оценка наборов запросов эффективна, особенно при работе со связанными моделями. Это включает использование select_related или prefetch_related, где это уместно, чтобы минимизировать запросы к базе данных
Настройте поведение виджета: В зависимости от потребностей фронтенда, вы можете настроить поведение виджета. Например, если вам нужен выпадающий выбор для нескольких вариантов, вы можете использовать SelectMultiple вместо CSVWidget
Упрощение определений фильтров: Если определение фильтра становится сложным, подумайте о том, чтобы разбить его на более мелкие классы или функции фильтра, которые можно использовать повторно. Это может улучшить читаемость и сопровождаемость кода
# views.py
from django.db.models import Prefetch
class ShortFormListCreateView(generics.ListCreateAPIView):
queryset = A.objects.select_related('user').prefetch_related(
Prefetch('b_set', queryset=B.objects.select_related('c'))
)
filter_backends = [DjangoFilterBackend]
filterset_class = AFilterSet
# filters.py
from django_filters import Filter, FilterSet
class NameFilter(Filter):
def filter(self, qs, value):
if value:
names = value.split(',') # Assuming CSV input
return qs.filter(b__c__name__in=names)
return qs
class AFilterSet(FilterSet):
aa = NameFilter(field_name='b__c__name', label='C Names')
class Meta:
model = A
fields = ['aa']