Django model elapsed time between two DateTimeFields - django

I am pretty new to django and haven't been able to find a way to get the elapsed time between two DateTimeFields and save it to another model.
from django.db import models
class Timesheet(models.Model):
startTime = models.DateTimeField(auto_now_add=True)
endTime = models.DateTimeField(blank=True, null=True)
duration = models.DateTimeField(endTime - startTime)
def _str_(self):
return self.startTime
How can I make duration = endTime - startTime?
I am also using a PostgreSQL database.

I wouldn't use a dedicated model field for the duration.
I would use a property on the model instead for the same functionality.
Something like:
#property
def duration(self)
return self.end_time - self.startime
Lucas has a good idea of using an annotation, but if you have a Timesheet instance somewhere that didn't come from that object manager and was not previously annotated, you would have to do a separate database hit to actually annotate it.
This property is used as such:
some_timesheet_instance.duration

Use annotate() to compute the duration field at query time for each object in the queryset
from django.db.models import F, ExpressionWrapper, fields
timesheets = Timesheet.objects.annotate(
duration=ExpressionWrapper(
F('endTime') - F('startTime'),
output_field=fields.DurationField()
)
)
timesheets[0].duration # datetime.timedelta(0, 722, 18373)
Is possible perform another queryset methods over annotations like filter(), order_by(), aggregate(), etc.
timesheets.order_by('-duration')
timesheets.aggregate(Avg('duration')) # {'duration__avg': datetime.timedelta(0, 26473, 292625)}

duration = timesheet.end_time - timesheet.start_time
When you substract two datetime instances you don't get another datetime instance but a timedelta instace, which is just the days, seconds and microseconds difference between the two datetimes. You can't store a timedelta in a DateTimefield, but you can use an IntegerField, for example:
days_in_seconds = duration.days * 86400 # days difference by seconds in a day
duration_in_seconds = duration.seconds + days_in_seconds # duration in seconds
When you want to access the duration as timedelta you just do:
import datetime
duration = datetime.timedelta(seconds=timesheet.duration)
You can also store it as FloatField as suggested in this question.

Related

check if today is due date django

I have a model like this
class Tasks(models.Model):
title = models.CharField(max_length=100,null=True,blank=True)
due_date_time= models.DateTimeField(default=timezone.now)
As due date is a date and time field, how I can check if today is due date of this task , while I am saving time and date both
You can make use of the __date lookup [Django-doc]:
from django.utils.timezone import now
Tasks.objects.filter(
due_date_time__date=now().date()
)
or if you work with timezones, you can work with a range check:
from datetime import timedelta
from django.utils.timezone import now
today = now().replace(hour=0, minute=0, second=0, microsecond=0)
tomorrow = today + timedelta(days=1)
Tasks.objects.filter(
due_date_time__gte=today,
due_date_time__lt=tomorrow
)
Note: normally a Django model is given a singular name, so Task instead of Tasks.

Adding a field to queryset in django

I have a model whose fields are datetimefields start_time and end_time
I want to display on the API- start_time, end_time and the difference between them
However, I don't want to use for or while loop to achieve this because it is too slow
How can I get the difference on the API without looping
You can annotate the queryset with the difference, and then use an ExpressionWrapper to convert this to a DurationField, and thus obtain a timedelta objects:
from django.db.models import DurationField, ExpressionWrapper, F
MyModel.objects.annotate(
time_diff=ExpressionWrapper(
F('end_time')-F('start_time'),
output_field=DurationField()
)
)
The MyModels that arise from this queryset will thus have an extra attribute time_diff that is a timedelta field containing the difference between end_time, and start_time.

Django/Python How do I get my receiver to add a value to a field relative to a previous addition - instead of indiscriminately adding to the value?

Python3.6.4; Django 2.0. Long time first time, please be gentle
I'm creating a tracking application that accepts multiple work logs to a work order. I'm trying to track time spent on a workorder by obtaining a time delta from each log and sending it to the work order model to hold the aggregate time.
The problem is, each time I'm updating an existing log it adds the entire time to the work order instead of just the difference. So if a log was previously 12-2:00 (feeds 2 hours to WorkOrder), and you changed it to 12-1:30 it was feed an additional 1.5 hours to WorkOrder, instead of subtracting 30min
Is there a way I can check to see a time was previously sent to WorkOrder? I tried updating update_labor_hours to check if the timedelta was < or > the original time, but I couldn't really figure it out.
Any help is appreciated!
from django.utils.timezone import now
from datetime import datetime, date, timedelta
from django.db import models
from django.db.models.signals import post_save
# Create your models here.
class WorkOrder(models.Model):
labor_hours = models.DurationField(blank=True, null=True)
class WorkLog(models.Model):
work_order = models.ForeignKey(WorkOrder, on_delete=models.PROTECT)
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
def _time_spent(self):
return datetime.combine(date.today(), self.end_time) - datetime.combine(date.today(), self.start_time)
time_spent = property(_time_spent)
def update_labor_hours(sender, instance, *args, **kwargs):
instance.work_order.labor_hours = instance.work_order.labor_hours + instance.time_spent
instance.work_order.save()
post_save.connect(update_labor_hours, sender=WorkLog)

Timedelta between two fields

I'm looking for objects where the timedelta between two fields is greater than a certain number of days.
Baiscally I have a date when a letter is sent, and a date when an approval is received. When no approval is received in let's say 30 days, then these objects should be included in the queryset.
I can do something like the below, where the delta is something static.
However I don't need datetime.date.today() as a start but need to compare against the other object.
delta = datetime.date.today() - datetime.timedelta(30)
return qs.filter(letter_sent__isnull=False)\
.filter(approval_from__isnull=True)\
.filter(letter_sent__gte=delta)
Any pointer how to do this?
Sounds like you want to annotate with an F object. Something like this:
from django.db.models import DurationField, ExpressionWrapper, F
delta = datetime.timedelta(days=30)
expression = F('approval_from') - F('letter_sent')
wrapped_expression = ExpressionWrapper(expression, DurationField())
qs = qs.annotate(delta=wrapped_expression)
qs = qs.filter(delta__gte=delta)
You can also make it the other way around. Just keep F("num_days") outside of timedelta because timedelta doesn't know about F().
from datetime import timedelta
expression = F('approval_from') - timedelta(days=1) * F("num_days")
wrapped_expression = ExpressionWrapper(expression, DateTimeField())
qs = qs.annotate(letter_sent_annotation=wrapped_expression)
qs = qs.filter(letter_sent__gte=letter_sent_annotation)

How should I use DurationField in my model?

In my model I want to be able to input duration, like 2 years, 5 months, etc.
In version 1.8 DurationField was introduced so I tried using that:
In my model I have
user_validPeriod = models.DurationField()
Trying to add a new User from my admin panel, If I try typing something like 2d or 2 days in the appearing text-field though I get Enter a valid duration.
Can someone provide me with an example of how this field is supposed to be used?
To use a DurationField in django 1.8 you have to use a python datetime.timedelta instance like this:
Considering this model :
from django.db import models
class MyModel(models.Model):
duration = models.DurationField()
You can set a duration this way :
import datetime
my_model = MyModel()
my_model.duration = datetime.timedelta(days=20, hours=10)
And query it this way :
# Equal
durations = MyModel.objects.filter(duration=datetime.timedelta(*args, **kwargs))
# Greater than or equal
durations = MyModel.objects.filter(duration__gte=datetime.timedelta(*args, **kwargs))
# Less than or equal
durations = MyModel.objects.filter(duration__lte=datetime.timedelta(*args, **kwargs))
More info on datetime.timedelta here and on DurationField here.
In your admin panel, you can enter a duration with a string with following format : [DD] [[hh:]mm:]ss