Django .first() method cuts first digit from a Decimal

Question:

I am seeing really strange behavior when using .first() method on a QuerySet. After calling the method, first digit from a Decimal value gets cut.

Models:

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)

Monthly values:

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

Code:

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])

The outputs are as follows:

<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')}

As you can see for id 1 the original value is 519 but after I call the first() method it gets turned into a 19. This does not happend if I manually call the first item in QueySet using [0].

Any idea what might be causing this issue?

Thanks in advance 🙂

Asked By: Roitko

||

Answers:

the problem is that the function first use FIRST, TOP, or LIMIT in SQL query and SUM in the select.

Django executes the query only when you need the value, so the first() executes all the query and filters elements that the sum uses.

You can see de sql query with print(queryset.query).

In your code, the sql is SELECT CAST(SUM("app_distributions"."value_in_units") AS NUMERIC) AS "tsum" FROM "app_distributions" WHERE "app_distributions"."id" = N GROUP BY "app_distributions"."distribution_type_id" "app_distributions"."activity_date" (or similar), and when you use first(), django add more sql and only get the first record and sum only this record.

Answered By: FedeG
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.