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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:33:16