DRF и django_filters. Изменение набора запросов перед применением фильтров

Есть ModelViewSet и FilterSet, которые отлично работают. Но проблема в том, что мне нужно преобразовать queryset перед фильтрацией, для этого я переопределил метод get_queryset(). Он изменяет queryset, но в результате на странице со списком объектов я вижу, что никаких изменений не произошло.

Если я переопределю метод list(), используя в нем переписанный метод get_queryset():

queryset = self.get_queryset()

Изменения произойдут, но фильтры не будут работать на этом наборе запросов. Я пробовал использовать:

qs = self.filter_queryset(self.get_queryset())

в этом случае фильтры работают, но данные на странице остаются такими, какими они были до изменения в get_queryset(). Подскажите, пожалуйста, в чем моя ошибка, почему я не могу фильтровать кверисет с изменениями и что нужно сделать, чтобы фильтрация преобразованного кверисета стала возможной?

EDIT:

views.py

from .serializers import OfferSerializer
from rest_framework import viewsets
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Offer


def validate(price:str, term:str, deposit:str) -> bool:
    """ Validating type of parameters. """
    try:
        price, term, deposit = int(price), int(term), int(deposit)
        return True
    except:
        return False

def calculate_payment(qs, price:str, term:str, deposit:str):
    """ Calculating payment by entry parameters and bank rate. """
    clear_payment = (int(price) - int(deposit)) / (int(term) * 12)
    for el in qs:
        rate_multiplier = (el.rate + 100) / 100
        el.payment = clear_payment * rate_multiplier
    

class OfferFilter(filters.FilterSet):
    payment_min = filters.NumberFilter(field_name="payment", lookup_expr='gte')
    payment_max = filters.NumberFilter(field_name="payment", lookup_expr='lte')
    rate_min = filters.NumberFilter(field_name="rate", lookup_expr='gte')
    rate_max = filters.NumberFilter(field_name="rate", lookup_expr='lte')
    bank_name = filters.CharFilter(field_name="bank_name", lookup_expr='contains')

    order_by = filters.OrderingFilter(
        fields=(
            ('payment', 'payment'),
            ('bank_name', 'bank_name'),
            ('rate', 'rate'),
        ),
    )
    class Meta:
        model = Offer
        fields = ('payment_min', 'payment_max', 'rate_min', 'rate_max', 'bank_name')
    

class OfferViewSet(viewsets.ModelViewSet):
    serializer_class = OfferSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = OfferFilter
    filterset_fields = ['rate', 'payment', 'bank_name']

    def get_queryset(self):
            queryset = Offer.objects.all()
            query_params = dict(self.request.GET.items())
            if 'price' in query_params and 'term' in query_params:
                deposit = query_params['deposit'] if 'deposit' in query_params else 0
                price = query_params['price']
                term = query_params['term']
                if validate(price, term, deposit):
                    calculate_payment(queryset, price, term, deposit)
            return queryset

    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

    def list (self, request):
        queryset = self.get_queryset()
        queryset = self.filter_queryset(queryset)
        serializer = OfferSerializer(queryset, many=True)
        return Response(serializer.data)

Вам нужно аннотировать набор запросов вместо того, чтобы итерировать его и устанавливать атрибуты

from django.db.models import F, Value

...

    def get_queryset(self):
        queryset = Offer.objects.all()
        query_params = dict(self.request.GET.items())
        if 'price' in query_params and 'term' in query_params:
            deposit = query_params['deposit'] if 'deposit' in query_params else 0
            price = query_params['price']
            term = query_params['term']
            if validate(price, term, deposit):
                clear_payment = (int(price) - int(deposit)) / (int(term) * 12)
                queryset = queryset.annotate(
                    rate_multiplier=(F('rate') + Value(100.0)) / Value(100.0)
                ).annotate(
                    payment=Value('clear_payment') * F('rate_multiplier')
                )
            return queryset

Если Django не может определить правильный тип возврата из аннотаций, вам может потребоваться обернуть их в ExpressionWrapper для указания типа возврата

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