Django按日期范围查询时如何基于balance字段生成累计余额CumBalance列
问题原因
你之前的实现方式存在以下问题:
annotate(cumbalance =Sum('balance'))是对单行的balance字段求和,计算结果和原balance字段完全一致,无法得到累计值aggregate(Sum('balance'))会直接返回整个查询集的总余额数值,无法和每一行明细数据对应
解决方案1:数据库层面实现(推荐,性能更高)
Django 2.0及以上版本支持窗口函数,可以直接在SQL层面计算累计值,适合数据量较大的场景:
首先导入需要的方法:
from django.db.models import Sum, Window, F
修改视图中的查询代码:
def partyDetails(request,pk): form = DateRangeForm() cumulative_balance = [] if request.method == 'POST': form = DateRangeForm(request.POST) if form.is_valid(): start = form.cleaned_data['start_date'] end = form.cleaned_data['end_date'] cumulative_balance = PurchasePayment.objects.filter( vendor=pk, date__range=(start, end) ).order_by('date', 'id').annotate( # 按日期+id排序,避免同一天数据顺序错乱 cumbalance=Window( expression=Sum('balance'), order_by=[F('date').asc(), F('id').asc()], frame=Window.rows_between(Window.UNBOUNDED_PRECEDING, Window.CURRENT_ROW) ) ) else: return redirect('party_ledger') return render(request, 'purchase/party_details.html', { 'dateform':form, 'cumbal':cumulative_balance, 'name':pk })
解决方案2:Python层面遍历实现(兼容低版本Django,适合小数据量)
如果Django版本不支持窗口函数,或者查询数据量很小,可以直接在Python代码中遍历计算累计值:
def partyDetails(request,pk): form = DateRangeForm() cumulative_balance = [] if request.method == 'POST': form = DateRangeForm(request.POST) if form.is_valid(): start = form.cleaned_data['start_date'] end = form.cleaned_data['end_date'] payment_list = PurchasePayment.objects.filter( vendor=pk, date__range=(start, end) ).order_by('date', 'id') cum_total = 0 for item in payment_list: cum_total += item.balance item.cumbalance = cum_total cumulative_balance.append(item) else: return redirect('party_ledger') return render(request, 'purchase/party_details.html', { 'dateform':form, 'cumbal':cumulative_balance, 'name':pk })
模板渲染
两种方案最终返回的cumbal对象都可以直接在模板中调用cumbalance属性展示累计余额:
{% for item in cumbal %} <tr> <td>{{ item.amount }}</td> <td>{{ item.payment }}</td> <td>{{ item.balance }}</td> <td>{{ item.cumbalance }}</td> </tr> {% endfor %}
内容的提问来源于stack exchange,提问作者Shanzedul
相关产品推荐
相关产品推荐

