I have the following model:
class Item(models.Model):
last_used = models.DateTimeField()
created_at = models.DateTimeField()
I'm trying now to look at the average "lifetime" of items per month. Lifetime is how many days items are used, on average. I can calculate the overall average with the following:
from django.db.models import IntegerField, F, Avg
from django.db.models.functions import Cast, ExtractDay, TruncDate
items = Item.objects.annotate(days_used=Cast(ExtractDay(TruncDate(F('last_used')) - TruncDate(F('created_at'))), IntegerField()) 1)
avg = items.aggregate(Avg('days_used'))['days_used__avg']
Now when I want to calculate this per month, I would like to annotate the query with a new field that is the maximum of created_at or the beginning of the month in question. This is needed so that the maximum lifetime value for all months is the number of days in that month.
There's a function, Greatest that I think could be used for this, but I'm not sure how. Assuming we're interested in December, 2021, this is what I have at the moment:
from django.db.models.functions import Greatest
target_month = 12
items = items.annotate(created_max=Greatest(TruncDate(F('created_at')), TruncDate(F(timezone.datetime(2021, target_month, 1)))
But using timezone.datetime is causing an error:
AttributeError: 'datetime.datetime' object has no attribute 'split'
NOTE: I'm aware that last_used may cause the value to go over the number of days but that's another issue we're tackling.
Solved
Thanks to @Eugenij this using Value instead of F works:
items.annotate(created_max=Greatest(TruncDate(F('created_at')), Value(timezone.datetime(2021, target_month, 1)))
CodePudding user response:
You should use django.db.models.Value expression instead of django.db.models.F for constant values.
from django.db.models.functions import Greatest
target_month = 12
items = items.annotate(created_max=Greatest(TruncDate(F('created_at')), TruncDate(Value(timezone.datetime(2021, target_month, 1)))
