You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义Django Filter实现筛选结果的求和统计?

问题描述

默认情况下,GET请求api/v1/analytics/需返回当前用户所有店铺的已售商品数、税费、应计金额的总和;若用户传入stores_ids=1,2这类指定店铺ID的参数,则需返回对应店铺的统计总和。现有filters.py代码存在问题,需编写正确的实现代码。


相关代码参考

模型代码

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}"

视图代码

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

现有问题的filters.py代码

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

问题分析

  1. 原视图中get_queryset直接返回聚合字典,Django Filter无法作用于非QuerySet对象,导致过滤逻辑失效。
  2. 自定义filter方法存在参数错误与未定义变量问题,逻辑实现不符合FilterSet规范。
  3. 未将用户权限过滤(仅能查看自己的店铺)与参数过滤整合,存在越权风险。

正确实现方案

调整后的filters.py

from dashboard.models import StoreAnalytics
from django_filters import rest_framework as filters

class StoreAnalyticsFilter(filters.FilterSet):
    stores_ids = filters.CharFilter(method='filter_stores_ids')

    class Meta:
        model = StoreAnalytics
        fields = ['stores_ids']

    def filter_stores_ids(self, queryset, name, value):
        # 分割传入的店铺ID列表并转为整数
        store_ids = [int(id.strip()) for id in value.split(',') if id.strip().isdigit()]
        return queryset.filter(store_id__in=store_ids)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 初始化时过滤当前用户的店铺数据,确保权限控制
        self.queryset = self.queryset.filter(store__user_id=self.request.user.pk)

对应的视图修改

class StoreAnalyticsApi(ListCreateAPIView):
    permission_classes = (IsAuthenticated,)
    http_method_names = ["get"]
    serializer_class = StoreAnalyticsSerializer
    filter_backends = (DjangoFilterBackend,)
    filterset_class = StoreAnalyticsFilter
    
    def get_queryset(self):
        # 先获取经过过滤器处理的QuerySet
        filtered_qs = super().get_queryset()
        # 对过滤后的QuerySet执行聚合操作
        aggregated_data = filtered_qs.aggregate(
            accruals_sum=Sum(F("accruals_sum")),
            sold_products=Sum(F("sold_products")),
            taxes_summ=Sum(F("taxes_count"))
        )
        # 将聚合结果包装为列表,适配ListAPIView的返回要求
        return [aggregated_data]

实现说明

  1. 过滤器通过__init__方法提前过滤当前用户的店铺数据,确保用户只能访问自己的店铺,避免越权。
  2. filter_stores_ids方法处理stores_ids参数,分割并校验ID格式后过滤对应店铺。
  3. 视图先获取经过过滤的QuerySet,再执行聚合操作,最后将结果包装为列表以适配ListCreateAPIView的返回要求。

内容的提问来源于stack exchange,提问作者Iren E

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:32:00