如何通过Django模型查询本月销售额较上月增长超20%的公司
实现本月销售额较上月增长超20%的公司查询
要实现这个需求,我们可以通过Django的子查询、注解(annotate)和字段表达式(F())关联每个公司的本月与上月销售额,再筛选出符合增长条件的公司。以下是具体实现方案:
前提说明
假设每个公司每月仅生成一份MonthlyReport,如果存在同一公司一月多份报告的情况,需要将代码中的单条销售额替换为月度销售额总和(使用Sum聚合函数)。
实现步骤
1. 导入必要模块
from django.db.models import F, Subquery, OuterRef, Sum from django.utils import timezone from datetime import timedelta from .models import Company, MonthlyReport
2. 计算本月与上月的日期范围
先确定当前月份和上月的起止日期,确保日期过滤的准确性:
today = timezone.now().date() # 本月起始日期(当月1号) this_month_start = today.replace(day=1) # 下月起始日期(用于过滤本月数据,date__lt=next_month_start) next_month_start = (this_month_start + timedelta(days=32)).replace(day=1) # 上月结束日期(本月1号前一天) last_month_end = this_month_start - timedelta(days=1) # 上月起始日期(上月1号) last_month_start = last_month_end.replace(day=1)
3. 定义子查询获取月度销售额
分别定义子查询,获取每个公司的本月销售额和上月销售额:
# 子查询:获取每个公司的本月销售额(若多份报告则求和) this_month_sales = MonthlyReport.objects.filter( company=OuterRef('pk'), date__gte=this_month_start, date__lt=next_month_start ).aggregate(total=Sum('sale')).values('total')[:1] # 子查询:获取每个公司的上月销售额(若多份报告则求和) last_month_sales = MonthlyReport.objects.filter( company=OuterRef('pk'), date__gte=last_month_start, date__lte=last_month_end ).aggregate(total=Sum('sale')).values('total')[:1]
4. 查询符合条件的公司
通过注解关联两个子查询的结果,筛选出增长超过20%的公司(同时排除上月销售额为0的情况,避免计算错误):
target_companies = Company.objects.annotate( current_sale=Subquery(this_month_sales), prev_sale=Subquery(last_month_sales) ).filter( prev_sale__gt=0, # 排除上月无销售额或销售额为0的公司 current_sale__gt=F('prev_sale') * 1.2 # 本月销售额 > 上月的120% )
可选:通过MonthlyReport自连接实现
也可以直接从MonthlyReport入手,关联本月和上月的记录,再反向获取公司:
# 先筛选出本月的报告,注解上月销售额 qualified_reports = MonthlyReport.objects.filter( date__gte=this_month_start, date__lt=next_month_start ).annotate( prev_month_sale=Subquery( MonthlyReport.objects.filter( company=F('company'), date__gte=last_month_start, date__lte=last_month_end ).aggregate(total=Sum('sale')).values('total')[:1] ) ).filter( prev_month_sale__gt=0, sale__gt=F('prev_month_sale') * 1.2 ) # 从符合条件的报告中提取对应的公司 target_companies = Company.objects.filter(id__in=qualified_reports.values('company__id')).distinct()
注意事项
- 若业务允许公司某月无报告,可根据需求调整过滤条件(比如保留
current_sale或prev_sale为None的情况,但需处理空值逻辑)。 - 日期处理使用
django.utils.timezone确保时区一致性,避免因服务器时区导致的日期偏差。
内容的提问来源于stack exchange,提问作者kazem qanati
相关产品推荐
相关产品推荐

