我想将数据导出到Django中的Excel文件。首先,我得到所有成员:
members_list = Member.objects\
.exclude(deleted=True) \
.annotate(num_calculations=Count('user__calculations'))\
.order_by("-user__date_joined")
然后我遍历成员并将它们添加到excel文件中:
for row in members_list:
row_num += 1
ws.write(row_num, 0, row.number or "", font_style)
ws.write(row_num, 1, row.user.first_name or "", font_style)
ws.write(row_num, 2, row.user.last_name or "", font_style)
ws.write(row_num, 3, row.address or "", font_style)
col_num = 3
for calculation in calculations_per_month_last_12_months(row.number):
col_num += 1
ws.write(row_num, col_num, calculation['total_calculations'] or "", font_style)
函数calculations_per_month_last_12_months
如下:
items = list(Calculations.objects
.filter(user__member__number=member_number)
.filter(price_date__gte=datetime.datetime.now().today() - relativedelta(months=12))
.annotate(date=TruncMonth('price_date'))
.values('date')
.annotate(total=Count('id'))
.values('date', 'total')
.order_by('date'))
result = []
for month in range(12):
date = timezone.now() - relativedelta(months=month)
month_results = list(filter(lambda i: date <= i['date'] + relativedelta(months=1) < (date + relativedelta(months=1)), items))
month_result = 0
if month_results:
month_result = month_results[0]['total']
result.append({
'total_calculations': month_result
})
return result
一切正常,但问题是我是否有大量成员。
calculations_per_month_last_12_months
函数会转到每个成员的数据库。这就是我不想要的。
如果我有5000个成员,那么它将进入数据库5000次。
任何建议我怎么能避免它?
答案 0 :(得分:0)
您可以使用prefetch_related
获取原始查询中的所有计算。它本质上执行SQL连接,以便它们都成为一个查询。我还添加了select_related('user')
以避免对成员的用户名称进行额外查询。
members_list = Member.objects\
.exclude(deleted=True) \
.annotate(num_calculations=Count('user__calculations'))\
.order_by("-user__date_joined")\
.select_related("user")\
.prefetch_related("user__calculations")
然后在您的函数中,您可以传入member
对象并且它已经预先加载了计算,因此您可以跳过其他查询
def calculations_per_month_last_12_months(member):
for month in range(12):
date = timezone.now() - relativedelta(months=month)
next_month = date + relativedelta(months=1)
month_results = list(
filter(
lambda i: date <= i['date'] + relativedelta(months=1) < next_month,
member.user.calculations
)
)
month_result = 0
if month_results:
month_result = month_results[0]['total']
yield {'total_calculations': month_result}
编辑:还添加了yield
语法,以避免为结果列表分配额外空间。