Django queryset return DurationField value in seconds
Question:
I have two models: Post
, Comment
(Comment has FK relation to Post).
Now I want to return all posts with theirs "response time". I get this response time in timedelta
format. Can I receive it in seconds instead? I tried ExtractSecond
but it is not what I’m looking for:
base_posts_queryset.annotate(
min_commnet_date=Min("comment_set__date_created"),
response_time=ExpressionWrapper(F("min_commnet_date") - F("date_created"), output_field=DurationField()),
response_time_in_sec=ExtractSecond(F("response_time"))
).filter(response_time__isnull=False).values("response_time", "response_time_in_sec")
This code returns following objects:
{'response_time': datetime.timedelta(days=11, seconds=74024, microseconds=920107), 'response_time_in_sec': 44}
What I want to achieve is basically call .seconds
for each item in result queryset. I could do this in python, but mb it could be done on db level?
Answers:
Sure can, but the exact mechanism may depend upon your database.
In postgres, you can use EXTRACT(epoch FROM <interval>)
to get the total number of seconds.
To use this in Django, you can create a Func
subclass:
class Epoch(django.db.models.expressions.Func):
template = 'EXTRACT(epoch FROM %(expressions)s)::INTEGER'
output_field = models.IntegerField()
Then you can use it directly:
base_posts.annotate(
response_time_sec=Epoch(F('min_comment_date') - F('date_created'))
)
Nice solution!
One wrinkle is that I think there is a missing ‘s’ needed to get this to work in Django 3
class Epoch(django.db.models.expressions.Func):
template = 'EXTRACT(epoch FROM %(expressions)s)::INTEGER'
output_field = models.IntegerField()
As already answered here, it depends upon your database. As stated in Django documentation of Extract method:
Django usually uses the databases’ extract function, so you may use any lookup_name that your database supports.
So for example with PostgreSQL:
response_time_in_sec=Extract(F("response_time"), "epoch")
I have two models: Post
, Comment
(Comment has FK relation to Post).
Now I want to return all posts with theirs "response time". I get this response time in timedelta
format. Can I receive it in seconds instead? I tried ExtractSecond
but it is not what I’m looking for:
base_posts_queryset.annotate(
min_commnet_date=Min("comment_set__date_created"),
response_time=ExpressionWrapper(F("min_commnet_date") - F("date_created"), output_field=DurationField()),
response_time_in_sec=ExtractSecond(F("response_time"))
).filter(response_time__isnull=False).values("response_time", "response_time_in_sec")
This code returns following objects:
{'response_time': datetime.timedelta(days=11, seconds=74024, microseconds=920107), 'response_time_in_sec': 44}
What I want to achieve is basically call .seconds
for each item in result queryset. I could do this in python, but mb it could be done on db level?
Sure can, but the exact mechanism may depend upon your database.
In postgres, you can use EXTRACT(epoch FROM <interval>)
to get the total number of seconds.
To use this in Django, you can create a Func
subclass:
class Epoch(django.db.models.expressions.Func):
template = 'EXTRACT(epoch FROM %(expressions)s)::INTEGER'
output_field = models.IntegerField()
Then you can use it directly:
base_posts.annotate(
response_time_sec=Epoch(F('min_comment_date') - F('date_created'))
)
Nice solution!
One wrinkle is that I think there is a missing ‘s’ needed to get this to work in Django 3
class Epoch(django.db.models.expressions.Func):
template = 'EXTRACT(epoch FROM %(expressions)s)::INTEGER'
output_field = models.IntegerField()
As already answered here, it depends upon your database. As stated in Django documentation of Extract method:
Django usually uses the databases’ extract function, so you may use any lookup_name that your database supports.
So for example with PostgreSQL:
response_time_in_sec=Extract(F("response_time"), "epoch")