Django新手求助:如何用annotate统计关联模型的数量与金额
按币种分组展示投资组合统计的解决方案
嘿,作为Django新手遇到分组统计的问题太正常了,我来帮你一步步搞定这个需求!首先得理清楚模型关系(我会先假设一个常见的模型结构,你可以根据自己的实际代码调整),然后修正查询逻辑,最后在模板里展示数据。
第一步:确认你的模型结构(参考示例)
假设你的模型大概是这样的(如果和你的实际模型有出入,记得调整字段名和关联关系):
# models.py from django.db import models class Portfolio(models.Model): name = models.CharField(max_length=100) # 其他自定义字段... class Item(models.Model): portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='items') currency = models.CharField(max_length=20) # 存储币种名称,比如"门罗币"、"以太坊" # 其他字段... class Trade(models.Model): item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='trades') openm_price = models.DecimalField(max_digits=12, decimal_places=4) # 每枚币种的买入单价 close_price = models.DecimalField(max_digits=12, decimal_places=4) # 每枚币种的卖出单价 quantity = models.DecimalField(max_digits=10, decimal_places=2) # 交易的币种数量 # 其他字段(比如交易时间等)...
第二步:修正视图中的查询逻辑
你之前用Count('items__trade__openm_price')是错误的——Count是用来统计数量的,而你需要的是金额总和,应该用Sum。另外要按币种分组,必须先通过values('currency')指定分组字段,再用annotate做聚合统计。
下面是正确的视图代码:
# views.py from django.db.models import Count, Sum, F, ExpressionWrapper, DecimalField from django.shortcuts import render from .models import Item def portfolio_index(request): # 按币种分组,计算各项统计数据 currency_stats = Item.objects.values('currency').annotate( # 统计该币种的交易总数 total_trades=Count('trades'), # 计算总支付价格:买入单价 × 交易数量 的总和 total_paid=Sum( F('trades__openm_price') * F('trades__quantity'), output_field=DecimalField(max_digits=15, decimal_places=4) ), # 计算总收款:卖出单价 × 交易数量 的总和 total_received=Sum( F('trades__close_price') * F('trades__quantity'), output_field=DecimalField(max_digits=15, decimal_places=4) ) ).annotate( # 计算净盈亏:总收款 - 总支付价格 net_profit_loss=ExpressionWrapper( F('total_received') - F('total_paid'), output_field=DecimalField(max_digits=15, decimal_places=4) ) ) return render(request, 'portfolio/index.html', {'currency_stats': currency_stats})
关键说明:
values('currency'):告诉Django按currency字段分组统计F():用来引用模型字段的值,避免把字段当成字符串处理,支持字段间的运算(比如单价×数量)ExpressionWrapper:用来处理字段运算后的类型转换,确保Decimal类型的计算不会出错- 如果你的
openm_price已经是该笔交易的总支付金额(而不是单价),那直接用Sum('trades__openm_price')就行,不用乘以数量
第三步:在模板中展示数据
现在把统计结果传到index.html,用表格按币种一行一行展示:
<!-- portfolio/index.html --> <h1>我的投资组合统计</h1> <div class="stats-table"> <table> <thead> <tr> <th>币种</th> <th>交易总数</th> <th>总支付价格</th> <th>净盈亏</th> </tr> </thead> <tbody> {% for stat in currency_stats %} <tr> <td>{{ stat.currency }}</td> <td>{{ stat.total_trades }}</td> <td>{{ stat.total_paid|floatformat:2 }}</td> <td> {% if stat.net_profit_loss > 0 %} <span style="color: green;">+{{ stat.net_profit_loss|floatformat:2 }}</span> {% elif stat.net_profit_loss < 0 %} <span style="color: red;">{{ stat.net_profit_loss|floatformat:2 }}</span> {% else %} <span>0.00</span> {% endif %} </td> </tr> {% empty %} <tr> <td colspan="4" style="text-align: center;">暂无交易数据</td> </tr> {% endfor %} </tbody> </table> </div>
这样就能实现你想要的效果:每个币种一行,展示交易总数、总支付价格,还有带颜色区分的净盈亏啦!
内容的提问来源于stack exchange,提问作者Sharpless512
相关产品推荐
相关产品推荐

