Django F Date Expresion generated from static field - python

I am trying to get a series of ages from a persons list, but the age generated change with each query because is the age in a specific event so i can accomplish this with a simple loop, extracting the timedelta from the diference:
[ (event_date - user.birth_date).days/365.25 for user in User.objects.all() ]
event_date is always a datatime.date object anr user.birth_date too. I consider it a "Static"
field or constant because it is outside the database.
This gives me the correct results, but since I am doing this many times and i have other calculations to do I wanted to generate the ages from the database using the F() expresion.
``from django.db.models import ExpressionWrapper, fields
diference = ExpressionWrapper(
event_date- F('birth_date'),
output_field=fields.DurationField())
qs_ages= self.annotate(age_dist=diference)
this should give me a field named age_dist that will be a timedelta contains the total days between the two dates, so now I should do this and should give me the same result as above.
[ user.age_dist.days/365.25 for user in User.objects.all() ]
But this does not work, the result is a time delta of microseconds
What i am doing wrong? and how should I include the static value of event_date to the expression?
And... going beyond. Is there any way to get the days of the resultant time delta from the ExpressionWrapper?

since you're doing the operation on a datetime object it'll return you a datetime object which is converted to timedelta microseconds when you use DurationField as an the output field.
you can workaround this by doing everything in the database :
age = ExpressionWrapper(
Cast((event_date.date() - F('birth_date')) / 365.25 ,output_field=fields.PositiveIntegerField()),
output_field=fields.PositiveIntegerField()
)

Related

how to subtract a date from today in django model

models.py
import datetime as dt
class Campaign(models.Model):
enddate = models.DateField()
def rest_of_the_day(self):
now = dt.datetime.now().date()
print('printing now value')
print(now)
print(self.campaign_enddate)
return (self.campaign_enddate-now).days
in views.py
def landing_page(request):
campaigns = Campaign.objects.all().order_by('-id')
return render(request, 'core/landing_page.html',{'campaigns':campaigns})
in html template
<span>{{campaign.rest_of_the_day}}</span>
I'm trying to store an end date and show the days left to the end date in html file using rest_of_the_day function in models.py
for example : if end date is 30-01-2010 and today is 15-01-2020, i want the rest_of_the_day to show 15
however, i get a TypeError at / unsupported operand type(s) for -: 'NoneType' and 'datetime.date'
Another option is to use database functions in a query to add column to QuerySet result.
Here each campaign object in QuerySet result will have td column of type datetime.timedelta.
from django.db.models.functions import Extract, Now, Trunc
campaigns = Campaign.objects.all().order_by('-id').annotate(
td=F('enddate') - Now()
)
Or add Trunc Result td column will be datetime.timedelta object with only days
We also need to cast calculations between date and datetime to one type / output_field.
campaigns = Campaign.objects.all().order_by('-id').annotate(
td=Trunc(
F('enddate') - Now(),
'day',
output_field=models.DateField()
)
)
Note that you will be manipulating on datetime.timedelta object. Also, adding Trunc over different part (Now(), F('enddate'), whole expression, different combinations) may produce slightly different result due to how days will be rounded and substracted. You can experiment with that.
To get result as integer (days) Extract function can be added to the mix:
campaigns = Campaign.objects.all().order_by('-id').annotate(
td=Extract(
F('enddate') - Trunc(Now(), 'day', output_field=models.DateField()),
'day'
)
)
Here td will be Integer.
It seems that self.campaign_enddate is None. This makes sense, since it is not a field you defined. You can use the enddate:
class Campaign(models.Model):
enddate = models.DateField()
def rest_of_the_day(self):
return (self.enddate-dt.date.today()).days
You can however make use of the |timeuntil template filter [Django-doc] here:
<span>{{ campaign.enddate|timeuntil }}</span>
THis makes it more convenient to process the amount of time until a certain date(time) happens.

Sum TimeField hours/minutes with Pandas

I am trying to use Pandas to sum the time (hours, minutes) of a series. The data comes from a TimeField
class PhoneRecord ( models.Model ):
et = models.TimeField ( null=True, blank=True )
In python I get the record and convert to a dataframe.
phone = PhoneRecord.objects.all()
df = read_frame ( phone )
df.et = df.et.fillna ( '00:00:00' ) # some records are blank
df [ "time" ] = pd.to_datetime(df.et, format = '%H:%M:%S', errors = 'coerce')
this gives me the following output.
0 00:00:35
1 00:00:29
2 00:00:00
3 00:00:00
4 00:00:37
......
When I try to sum
df.time.sum ()
I get errors like: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'
What do I need to do to be able to sum and average the data.
Thank you.
You just need to run a custom 1-liner here to combine time objects into timedelta objects which can then be summed together. (see the "print" line)
from datetime import datetime, timedelta
import pandas as pd
phone = PhoneRecord.objects.all()
df = pd.DataFrame(list([i.__dict__ for i in phone])) # create pd.df from model query
df.et = df.et.fillna('00:00:00') # some records are blank
print(df.et)
print("SUM:", sum([datetime.combine(datetime.min, time) - datetime.min for time in df.et.tolist()], timedelta()))
You should get something like this:
0 00:00:20
1 00:00:20
2 00:00:50
3 00:00:30
4 00:00:20
SUM: 0:02:20
I had to change things a bit to get them to work on my end so hopefully, it is the same with you and your version of Pandas and Django. Hope this helps!
All computations, such as getting averages, counting etc. should be performed (if possible) using database engine. I don't know about underlying problem, but using pandas on the server-side to get average values is definitely overkill. You need to look at aggregation facility of Django.
Also, you probably need to restructure the model. If you need to store duration of a phone conversation, you can use FloatField instead, e.g.
class PhoneRecord(models.Model):
duration = models.FloatField(blank=True, default=0.0, help_text=_('duration in seconds'))
# other fields...
# also, you can set up the duration field with `editable=False`, and
# calculate its value each time the record is created
In this case you can use Avg:
from django.db.models import Avg
PhoneRecords.objects.all().aggregate(Avg('duration'))
and get something like this
{'duration_avg': 12.3}

Filter Django DB object with a manipulated keyword

I take a timestamp for my Institution class:
class Institution(models.Model):
timestamp_utc = models.DateTimeField()
If there is an entry in the DB that has the same year, month and date (not time), then I want to update the value of the entry. If not, then I want to create a new entry.
The conditional is as follows:
if Institution.objects.filter(timestamp_utc.strftime("%Y/%m/%d")=b['timestamp_utc'].strftime("%Y/%m/%d")).exists():
I am getting this error:
Exception Value: keyword can't be an expression
Is there a way to filter the DB object with a manipulated keyword?
You can just filter by the date range, i.e. time stamps that are great than or equal to the date, and less that the date + 1 day.
from datetime import relativedelta
date_start = b['timestamp_utc'].strftime("%Y-%m-%d")
date_end = (b['timestamp_utc'] + relativedelta(days=1)).strftime("%Y-%m-%d")
if Institution.objects.filter(
timestamp_utc__gte=date_start, timestamp_utc__lt=date_end
).exists():

Find objects with date and time less then 24 hours from now

I have model with two fields:
class Event(models.Model):
date = models.DateField(_(u'Date'))
time = models.TimeField(_(u'Time'))
I need to find all objects where date&time is in 24 hours from now.
I am able to do this when using DateTime field, but I am not sure how to achieve this when fields are separated. Thanks in advance.
For the simple case (not sure if all are simple cases though...), this should do the trick:
import datetime
today = datetime.datetime.now()
tomorrow = today + datetime.timedelta(days=1)
qs_today = queryset.filter(
date=today.date(),
time__gte=today.time(),
)
qs_tomorrow = queryset.filter(
date=tomorrow.date(),
time__lt=tomorrow.time(),
)
qs = qs_today | qs_tomorrow
As you state you can do what you want with a DateTimeField, but now with the separate fields, I understand your issue is how to combine them.
Looking at the docs for DateField - your date variable is a datetime.date instance and similarly for TimeField time is a datetime.time. You can convert these into a datetime.datetime by using combine()
import datetime as dt
datetime = dt.datetime.combine(date,time)
You now have the datetime object as you would have from DateTimeField. You say in the question you can do the 24 hour from now bit from there, although let me know in comments if you need that made explicit.
Caveat I combine will fail where one of the fields is None - you state this can't happen, so I haven't added any error checking or validation of this.
EDIT
It occurs to me that the problem may not be the combination, but adding the calculated field to the Event object. You could look at this Q&A, or this. In summary you define the calculated value in a function in your class and then make it a property - either with a decorator or a function call. There's an example in the docs, adapting for your case:
def _get_datetime(self):
'''Returns a combination of date and time as a datetime'''
return dt.datetime.combine(self.date,self.time)
datetime = property(_get_datetime)
This should behave in the same way as you would expect a DateTimeField to behave.
You can use Q objects to search for "yesterday after current time or today before current time":
from django.db.models import Q
from .models import Event
from datetime import datetime, timedelta
def get_event_during_last_day():
now = datetime.now()
today = now.date()
yesterday = (now - timedelta(day=1)).date()
time = now.time()
query_yesterday = Q(date=yesterday, time__gt=time)
query_today = Q(date=today, time__lt=time)
return Event.objects.filter(query_yesterday | query_today)

How to list all the objects with a specific date no matter the time in a DateTime Field

I Have a model like this
foo=models.char
bar=models.dateime
In wich several foos arrives in one day in different time. I need to list all the foos in a specific date, no matter the time they arrive.
I can't change the model, so splitting the bar in two fields(one for date and one for time) is out of reach right now :(
I personally used the range filter and the internal datetime timestamps for max/min. For example:
date = datetime.date.today()
YourModel.objects.filter(bar__range=(
datetime.datetime.combine(
date,
datetime.time.min
),
datetime.datetime.combine(
date,
datetime.time.max
),
))
Assuming a datetime format like so : YYYYMMDD HHmmss
eg : 20100611 171000 would be the 11th of June 1020, 5:10pm
Assuming a list of models with the following dates:
20100609 120000
20100611 161204
20100611 121204
20100611 191204
And you want all models for 20100611, in pseudo-code you could do :
for (model in models)
if (model.datetime >='20100611 000000' or model.datetime<='20100611 235959')
filtered_foos.add(model.foo)
This will effecttively ignore the time part of the date (sorry I don't have a python interpreter the syntax is clearly off but you should get the idea)

Categories

Resources