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

Django日期字段聚合问题:按门店每日统计销售额分组失效

问题分析与解决方案

核心问题

你的代码存在三个关键错误:

  1. 未关联当前门店:SalesSerializer中使用self.queryset调用的是全量销售数据,没有过滤当前门店的记录,导致所有门店返回相同的统计结果。
  2. 日期分组错误:ExtractWeekDay提取的是星期几(1-7),不是按自然日(年月日)统计,不符合“每日统计”的需求。
  3. 关联字段配置错误:Sales模型的ForeignKey参数写错(应该是related_name而非related_field),且StoreSerializer中many=False不符合一对多的关联逻辑。

步骤1:修正模型错误

首先修复Sales模型的参数错误,补全字段类型:

class Store(models.Model):
    store_name = models.CharField(max_length=100)
  
    class Meta:
        db_table = 'stores'

class Sales(models.Model): 
    # 修正related_field为related_name,补全BigIntegerField
    store = models.ForeignKey(Store, on_delete=models.CASCADE, related_name='sales')
    product_id = models.BigIntegerField()
    sale_date = models.DateTimeField()
    total_mrp = models.FloatField()

步骤2:正确实现按门店每日统计

推荐两种实现方式,按需选择:

方式一:SerializerMethodField 直接处理(简单直观)

在StoreSerializer中通过SerializerMethodField对当前门店的销售数据按日期分组求和:

from django.db.models import Sum
from django.db.models.functions import ExtractDate
from rest_framework import serializers

class StoreSerializer(serializers.ModelSerializer):
    # 自定义字段返回每日统计数据
    daily_sales = serializers.SerializerMethodField()

    class Meta:
        model = Store
        exclude = ['id']

    def get_daily_sales(self, obj):
        # 对当前门店的销售记录按日期分组,计算total_mrp总和
        stats = obj.sales.values(
            # 提取日期部分(忽略时分秒)
            sale_date=ExtractDate('sale_date')
        ).annotate(
            total_sale=Sum('total_mrp')
        ).order_by('sale_date')
        
        # 转换为列表返回(QuerySet转字典列表)
        return list(stats)

方式二:预加载分组数据(性能更优)

如果数据量较大,用Prefetch提前预加载分组后的销售数据,减少数据库查询次数:

from django.db.models import Sum, Prefetch, Q, F
from django.db.models.functions import ExtractDate
from rest_framework import serializers, viewsets

# 定义每日统计的序列化器
class DailySalesStatsSerializer(serializers.Serializer):
    sale_date = serializers.DateField()
    total_sale = serializers.FloatField()

class StoreSerializer(serializers.ModelSerializer):
    daily_sales = DailySalesStatsSerializer(many=True, read_only=True)

    class Meta:
        model = Store
        exclude = ['id']

class StoreViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Store.objects.prefetch_related(
        Prefetch(
            'sales',
            # 提前对销售数据按门店+日期分组求和
            queryset=Sales.objects.values(
                'store',
                sale_date=ExtractDate('sale_date')
            ).annotate(total_sale=Sum('total_mrp')).order_by('sale_date'),
            to_attr='daily_sales'
        )
    )
    serializer_class = StoreSerializer

关键说明

  • 使用ExtractDate替代ExtractWeekDay:确保按具体的年月日分组,而非星期几。
  • 基于当前门店对象查询:通过obj.sales获取当前门店的关联销售记录,避免全量数据查询。
  • 一对多关联配置:related_name='sales'让Store对象可以通过obj.sales直接访问关联的销售记录,many=True适配一对多的关联关系。

内容的提问来源于stack exchange,提问作者Haris Shah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 23:05:17