Adding a field to queryset in django - 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.

Related

Django Queryset Postgres

I am writing queryset that will return this type
date
total_shipping_fee
2021-04-16
5,000
2021-04-17
100,000
where
class Payments(models.Model):
created = models.DateTimeField()
.....
SELECT DATE(created) from payment_payment .... group by 'created' -> outputs a correct query
My question is how to query/ or cast
Payment.objects.filter(created_range=("2021-05-14", "2021-05-14")).values('created').annotate(total_shipping_fee=Sum('total_shipping_fee'))
so that I can have queryset in above raw sql. I think that is my problem to CAST DATE(created) in django queryset. Thanks
You can work with:
from django.db.models import F, Sum
Payment.objects.filter(
created__date_range=('2021-05-14', '2021-05-14')
).values(create_date=F('created__date')).annotate(
total_shipping_fee=Sum('total_shipping_fee')
).order_by('create_date')
here we thus first takes as values the truncated date of the DateTimeField, and then we use .order_by(…) [Django-doc] to enforce grouping by that date.
The output is a QuerySet of dictionaries, each with two items: { 'create_date': …, 'total_shipping_fee': … }.

Annotate a datetime field from another date time and duration in minutes

I want to annotate a datetime field to a queryset using timedelta and F objects
Example:
Model.objects.annotate(end_time=F('start_time')+timedelta(minutes=F('meeting_duration')))
Here I have a field "start_time" and "meeting_duration" in my table and I want to calculate and annotate an end_time using datetime.timedelta
And thereafter filter the queryset using the annotated field.
queryset.filter(end_time__gte=self.end_time)
Now I get this error:
TypeError: unsupported type for timedelta minutes component: F

Django model elapsed time between two DateTimeFields

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.

Django: Combine a date and time field and filter

I have a django model that has a date field and a separate time field. I am trying to use a filter to find a value on the latest record by date/time that is less than the current record's date time.
How do I use annotate/aggregate to combine the date and time fields into one and then do a filter on it?
models.py
class Note(models.model):
note_date = models.DateField(null=True)
note_time = models.TimeField(null=True)
note_value = models.PositiveIntegerField(null=True)
def get_last(n):
"""
n: Note
return: Return the note_value of the most recent Note prior to given Note.
"""
latest = Note.objects.filter(
note_date__lte=n.note_date
).order_by(
'-note_date', '-note_time'
).first()
return latest.note_value if latest else return 0
This will return any notes from a previous date, but if I have a two notes on the same date, one at 3pm and one at 1pm, and I send the 3pm note to the function, I want to get the value of the 1pm note. Is there a way to annotate the two fields into one for comparison, or do I have to perform a raw SQL query? Is there a way to convert the date and time component into one, similar to how you could use Concat for strings?
Note.objects.annotate(
my_dt=Concat('note_date', 'note_time')
).filter(
my_dt__lt=Concat(models.F('note_date'), models.F('note_time')
).first()
I am too late but here is what I did
from django.db.models import DateTimeField, ExpressionWrapper, F
notes = Note.objects.annotate(my_dt=ExpressionWrapper(F('note_date') + F('note_time'), output_field=DateTimeField()))
Now we have added a new field my_dt of datetime type and can add a filter further to do operations
Found an answer using models.Q here: filter combined date and time in django
Note.objects.filter(
models.Q(note_date__lt=n.note_date) | models.Q(
note_date=n.note_date,
note_time__lt=n.note_time
)
).first()
I guess I just wasn't searching by the right criteria.
Here is another Approach which is more authentic
from django.db.models import Value, DateTimeField
from django.db.models.functions import Cast, Concat
notes = Note.objects.annotate(my_dt=Cast(
Concat('note_date', Value(" "), 'note_time', output_field=DateTimeField()),
output_field=DateTimeField()
).filter(my_dt__lte=datetime.now())
Here is another solution following others.
def get_queryset(self):
from django.db import models
datetime_wrapper = models.ExpressionWrapper(models.F('note_date') + models.F('note_time'), output_field=models.DateTimeField())
return Note.objects.annotate(
note_datetime=datetime_wrapper
).filter(note_datetime__gt=timezone.now()).order_by('note_datetime')

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)