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

Django QuerySet的.first()方法截断Decimal值首数字问题求助

问题描述

使用Django的QuerySet .first()方法时,出现Decimal类型值首数字被截断的异常:调用.first()后值从519变为19,但用索引[0]能获取正确值。

模型定义

class Distributions(models.Model):
    distribution_id = models.AutoField(primary_key=True)
    months_duration = models.IntegerField(blank=True, null=True)
    month = models.IntegerField(blank=True, null=True)
    activity_date = models.DateField(blank=True, null=True)
    value_in_percentage = models.DecimalField(
        max_digits=8, decimal_places=4, blank=True, null=True
    )
    value_in_units = models.DecimalField(
        max_digits=16, decimal_places=4, blank=True, null=True
    )
    distribution_type = models.ForeignKey(
        DistributionType, on_delete=models.DO_NOTHING, blank=True, null=True
    )
    area = models.ForeignKey(Area, on_delete=models.DO_NOTHING, blank=True, null=True)
    is_active = models.BooleanField(blank=True, null=True)

月度值查询代码

data = Distributions.objects.all()
monthly_values = data.values("distribution_type_id", "activity_date").annotate(tsum=Sum("value_in_units"))

测试代码

def _get_month_value(self, id: int, month_values: QuerySet) -> int:
    print("----------")
    print(month_values)
    print(month_values.filter(id=id).values('tsum'))
    print(month_values.filter(id=id).values('tsum').first())
    print(month_values.filter(id=id).values('tsum')[0])

输出结果

<QuerySet [{'id': 1, 'tsum': Decimal('519')}, {'id': 3, 'tsum': Decimal('1019')}, 
{'id': 5, 'tsum': Decimal('0')}, {'id': 9, 'tsum': Decimal('0')}]>
<QuerySet [{'tsum': Decimal('519')}]>
{'tsum': Decimal('19')}
{'tsum': Decimal('519')}
原因分析

核心问题是字段混淆+QuerySet惰性执行与缓存的差异:

  1. 字段歧义:你的Distributions模型主键是distribution_id,但Django会自动为非id命名的主键添加id别名,所以filter(id=id)中的第一个id,实际指向的是模型的主键distribution_id,而非你看到的QuerySet结果里的id(这个id其实是distribution_type_id,属于输出时的显示错误)。

  2. QuerySet执行逻辑差异:

    • 当你打印month_values时,QuerySet会被立即执行并缓存结果,缓存的是按distribution_type_id和activity_date分组后的总和(比如distribution_type_id=1对应的总和是519)。
    • 调用month_values.filter(id=id).values('tsum')时,打印的是未执行的QuerySet的预期SQL描述,但实际调用.first()时,Django会重新构造SQL:过滤distribution_id=1的单条记录并求和,而这条记录的value_in_units恰好是19,所以得到结果19。
    • 用[0]取值时,Django直接使用了之前缓存的分组结果,在缓存数据中过滤id=1(对应distribution_type_id=1),因此得到正确的分组总和519。

简单来说:你以为filter的是分组后的distribution_type_id,但实际Django解析为过滤原模型的主键distribution_id,加上QuerySet的惰性执行特性,导致两种取值方式触发了不同的查询逻辑,得到了不同结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:57:08