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

如何用Django单查询集按月份统计两个模型的对象数量

解决方案

推荐方案:使用TruncMonth分组并补全缺失月份

这个方案能完美匹配你的目标格式,包含所有连续月份(即使某类车型无记录也显示0):

1. 导入所需依赖

from django.db.models import Count, F
from django.db.models.functions import TruncMonth
from django.utils.dateformat import DateFormat
from datetime import datetime

2. 分别统计两类车型的月度数据

单独统计避免跨模型关联导致的重复计数问题:

# 统计Car的月度数量
car_monthly = Car.objects.annotate(
    month=TruncMonth('car_purchase_date')
).values('month').annotate(
    car_count=Count('id')
).order_by('month')

# 统计Truck的月度数量
truck_monthly = Truck.objects.annotate(
    month=TruncMonth('truck_purchase_date')
).values('month').annotate(
    truck_count=Count('id')
).order_by('month')

3. 生成时间范围内的所有连续月份

确保不会遗漏任何月份:

# 获取数据覆盖的时间范围(自动取最早和最晚记录)
min_car_date = Car.objects.aggregate(min=F('car_purchase_date'))['min']
min_truck_date = Truck.objects.aggregate(min=F('truck_purchase_date'))['min']
min_date = min(min_car_date, min_truck_date) if min_car_date and min_truck_date else datetime.now()
max_date = datetime.now()

# 生成所有连续月份
current_month = min_date.replace(day=1)
all_months = []
while current_month <= max_date:
    all_months.append(current_month)
    # 切换到下一个月
    if current_month.month == 12:
        current_month = current_month.replace(year=current_month.year + 1, month=1)
    else:
        current_month = current_month.replace(month=current_month.month + 1)

4. 合并统计结果并格式化

把两类车型的数据匹配到对应的月份,生成最终统计列表:

# 转成字典快速查找
car_data = {item['month'].replace(day=1): item['car_count'] for item in car_monthly}
truck_data = {item['month'].replace(day=1): item['truck_count'] for item in truck_monthly}

# 生成最终统计数据
final_stats = []
for month in all_months:
    formatted_month = DateFormat(month).format('F Y')  # 格式化为"January 2023"
    final_stats.append({
        'month': formatted_month,
        'car_count': car_data.get(month, 0),
        'truck_count': truck_data.get(month, 0)
    })

5. 输出目标表格格式

如果需要打印成指定的表格样式:

# 打印表头
print("+---------------+-----------+-------------+")
print("|     Month     | Car Count | Truck Count |")
print("+---------------+-----------+-------------+")
# 打印每行数据
for stat in final_stats:
    print(f"| {stat['month']:<13} | {stat['car_count']:>9} | {stat['truck_count']:>11} |")
print("+---------------+-----------+-------------+")

简化方案:直接分组统计(不补全缺失月份)

如果不需要显示无记录的月份,可以直接按年月分组统计,代码更简洁:

from django.db.models import Count, IntegerField
from django.db.models.functions import ExtractYear, ExtractMonth

monthly_stats = (
    User.objects.annotate(
        year=ExtractYear('car__car_purchase_date', output_field=IntegerField()),
        month=ExtractMonth('car__car_purchase_date', output_field=IntegerField())
    )
    .union(
        User.objects.annotate(
            year=ExtractYear('truck__truck_purchase_date', output_field=IntegerField()),
            month=ExtractMonth('truck__truck_purchase_date', output_field=IntegerField())
        )
    )
    .values('year', 'month')
    .annotate(
        car_count=Count('car__id', distinct=True),
        truck_count=Count('truck__id', distinct=True)
    )
    .order_by('year', 'month')
)

注意:该方案不会自动补全没有任何记录的月份,需要额外处理才能匹配目标格式。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 01:12:37