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惰性执行与缓存的差异:
字段歧义:你的
Distributions模型主键是distribution_id,但Django会自动为非id命名的主键添加id别名,所以filter(id=id)中的第一个id,实际指向的是模型的主键distribution_id,而非你看到的QuerySet结果里的id(这个id其实是distribution_type_id,属于输出时的显示错误)。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
相关产品推荐
相关产品推荐

