Search code examples
pythondjangodjango-rest-frameworkdjango-filterdjango-filters

Custom django_filters (how to sum filters result)


I have an analytics model:

class StoreAnalytics(models.Model):
    class Meta:
        verbose_name_plural = "store's analytics"
        verbose_name = "store's analytics"

    store = models.ForeignKey(Store, on_delete=models.CASCADE, null=True, related_name="store_id")
    accruals_sum = models.DecimalField(
        max_digits=10, decimal_places=2, default=Decimal(0), verbose_name="Accruals sum"
    )
    taxes_count = models.DecimalField(max_digits=7, decimal_places=2, default=Decimal(0), verbose_name="Taxes sum")
    sold_products = models.PositiveSmallIntegerField(default=0, verbose_name="Number of products sold")

    def __str__(self):
        return f"{self.store.pk}"

My view:

class StoreAnalyticsApi(ListCreateAPIView):
    permission_classes = (IsAuthenticated,)
    http_method_names = ["get"]
    serializer_class = StoreAnalyticsSerializer
    filter_backends = (DjangoFilterBackend,)
    filterset_class = StoreAnalyticsFilter
    
    def get_queryset(self):
        queryset = StoreAnalytics.objects.filter(store__user_id=self.request.user.pk).aggregate(accruals_sum=Sum(F("accruals_sum")), sold_products=Sum(F("sold_products")), taxes_summ=Sum(F("taxes_count")))

        #{'accruals_sum': Decimal('10045'), 'sold_products': 68, 'taxes_summ': Decimal('602.700000000000')}
        return queryset

By default, the GET method api/v1/analytics/ should return the sum of sold products, sum of taxes and sum of accruals for all existing user's stores. If a user wants to view statistics only for chosen stores, then he selects the store ids and he should get the total analytics for the selected stores.

How should my filters.py look like to accruals tha aim?

My filters:

from dashboard.models import StoreAnalytics
from django_filters import rest_framework as filters
from django.db.models import Sum, F

class ListFilter(filters.Filter):
    def filter(self, qs, value):
        if not value:
            return qs

        self.lookup_expr = "in"
        values = value.split(",")
        return super(ListFilter, self).filter(qs, values)

class StoreAnalyticsFilter(filters.FilterSet):
    stores_ids = ListFilter(field_name="store_id")

    class Meta:
        model = StoreAnalytics
        fields = [
            "stores_ids",
        ]

    def filter(self, queryset, name, value):
        if value is not None:

            queryset = StoreAnalytics.objects.filter(store__user_id=self.request.user.pk, store_id__in=stores_ids)

        return queryset

I need somthing like that:

api/v1/analytics/ => sums of fields sold products, taxes, accruals_sum for ALL user's stores api/v1/analytics/?stores_ids=1,2 => sums of fields sold products, taxes, accruals_sum for user's stores with pk 1 & 2.


Solution

  • I could solve the problem by overriding get_queryset method:

    def get_queryset(self, request, *args, **kwargs):
        queryset = StoreAnalytics.objects.filter(store__user_id=self.request.user.pk)
        for backend in list(self.filter_backends):
            stores_sales = backend().filter_queryset(self.request, queryset, self).aggregate(accruals_sum=Sum(F("accruals_sum")), sold_products=Sum(F("sold_products")),  taxes_summ=Sum(F("taxes_count")))
    return queryset