I was trying to combine date and time. For that i was using datetime.combine. But it is giving AttributeError module 'datetime' has no attribute 'combine'
TimesheetEntry.objects.filter(
timesheet_users = user_id
).order_by(
'-id'
).annotate(
timesheet_clock_in=datetime.combine('timesheet_clock_in_date', 'timesheet_clock_in_time')
).annotate(
timesheet_clock_out=datetime.combine('timesheet_clock_out_date', 'timesheet_clock_out_time')
).values_list(
'timesheet_clock_in',
'timesheet_clock_out',
'timesheet_jobs',
'timesheet_note',
'timesheet_clock_in_by'
)
I know the error is in annotate but i don't how to solve it. Query works without annotate
In addition to this answer, use F() expressionas
from django.db.models import F
from datetime import datetime
TimesheetEntry.objects.filter(
timesheet_users=user_id
).order_by(
'-id'
).annotate(
timesheet_clock_in=datetime.combine(F('timesheet_clock_in_date'), F('timesheet_clock_in_time'))
).annotate(
timesheet_clock_out=datetime.combine(F('timesheet_clock_out_date'), F('timesheet_clock_out_time'))
).values_list(
'timesheet_clock_in',
'timesheet_clock_out',
'timesheet_jobs',
'timesheet_note',
'timesheet_clock_in_by'
)
Did you import datetime correctly?
import datetime
# The datetime module
or
from datetime import datetime
# The datetime class in the datetime module
In the first case you should call datetime.datetime.combine (with 2x datetime). In the second case you can call datetime.combine directly.
date.combine() does not work because it raises argument 1 must be datetime.date, not F
Here is how we approached this issue:
from django.db.models import Value
from django.db.models.functions import Cast, Concat
TimesheetEntry.objects.filter(
timesheet_users=user_id
).order_by(
'-id'
).annotate(
timesheet_clock_in=Cast(
Concat('timesheet_clock_in_date', Value(" "), 'timesheet_clock_in_time', output_field=DateTimeField()),
output_field=DateTimeField()
)
timesheet_clock_out=Cast(
Concat('timesheet_clock_out_date', Value(" "), 'timesheet_clock_out_time', output_field=DateTimeField()),
output_field=DateTimeField()
)
).values_list(
'timesheet_clock_in',
'timesheet_clock_out',
'timesheet_jobs',
'timesheet_note',
'timesheet_clock_in_by'
)
You can do it in the database instead of in the ORM:
TimesheetEntry.objects.filter(
timesheet_users = user_id
).order_by(
'-id'
).annotate(
timesheet_clock_in=ExpressionWrapper(F("timesheet_clock_in_date") + F("timesheet_clock_in_time"), output_field=DateTimeField())
).annotate(
timesheet_clock_out=ExpressionWrapper(F("timesheet_clock_out_date") + F("timesheet_clock_out_time"), output_field=DateTimeField())
).values_list(
'timesheet_clock_in',
'timesheet_clock_out',
'timesheet_jobs',
'timesheet_note',
'timesheet_clock_in_by'
)
+ is the Postgres operator for date + time → timestamp so make sure you're using the correct operator for your database.
Also as a note to anyone doing this: Be careful of timezone issues when combining date and time into timestamps.
Related
Let's say I have two models:
from django.db import model
class Company(model.Model):
name = models.TextField()
timezone = models.TextField()
class Sale(models.Model):
amount = models.IntegerField()
company = models.ForeignKey('Company')
time = models.DateTimeField()
I want to create a queryset grouped by date and company, where date refers to the calendar date of the sale at the timezone specified on the Company object.
This query:
result = Sale.objects.values(
'company', 'time__date'
).aggregate(
models.Sum('amount')
)
This returns the data in a format that works for me. However, the sales are grouped by UTC day. I want them grouped by the timezone on the Company objects.
What is the cleanest, quickest way to do this?
I know I could dump the entire set of values into Python, like this:
result = Sale.objects.values(
'amount', 'company__timezone', 'time'
).order_by(
'company_timezone'
)
for r in result:
r.date = r.time.astimezone(pytz.timezone(r.company_timezone)).date()
and then groupby, but is there a better way?
The solution is to use the TruncDate function, and pass the timezone string as an argument.
from django.db.models.functions import TruncDate
from django.db.models import F
...
local_time_daily_sales = Sale.objects.annotate(
date=TruncDate(tzinfo=F('company__timezone'))
).values(
date
).annotate(Sum('amount'))
from datetime import timedelta
from django.db.models import DateTimeField, ExpressionWrapper, F
MyModel.objects.annotate(
date_plus345=ExpressionWrapper(F('creation_date') + timedelta(days=345),
output_field=DateTimeField()
)
)
Like this is there any ways to add 40 months to the creation_date field and annotate it?
Does reative delata helps you?
from dateutil.relativedelta import relativedelta
MyModel.objects.annotate(
date_plus345=ExpressionWrapper(F('creation_date') + relativedelata(months+=40),
output_field=DateTimeField()
)
)
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.
I have the following queryset, It works well with the grouping by month.
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018')\
.annotate(date=TruncMonth('date_created'))\
.values('date').annotate(total_entries=Count('id'))
What I want is to group also by gender, here is a similar model with the gender field
class UserProfile:
date_created = models.DateTime(auto_now_add=True)
gender = models.CharField(max_length=3,choices=[('F',"Female"),('M',"Male")],default='M')
Expecting result:
May: 5 users [4(Male), 1(Female)]
June: 20 users [15(Male), 5(Female)]
For django < 2.0 you can use Conditional Expressions and Sum() to annotate the values you want:
from django.db.models import Sum, Case, When
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018').annotate(
date=TruncMonth('date_created'),
).values('date').annotate(
total_entries=Count('id'),
total_male=Sum(Case(When(gender='M', then=1), default=0, output_field=models.IntegerField())),
total_female=Sum(Case(When(gender='F', then=1), default=0, output_field=models.IntegerField())),
)
Since django 2.0 you can use Conditional Aggregation:
from django.db.models import Count, Q
from django.db.models.functions import TruncMonth
queryset = UserProfile.objects.filter(date_created__year='2018').annotate(
date=TruncMonth('date_created'),
).values('date').annotate(
total_entries=Count('id'),
total_male=Count('id', filter=Q(gender='M')),
total_female=Count('id', filter=Q(gender='F')),
)
I have a model:
class Document(models.Model):
expiry_date = models.DateField()
How can I build a query which fetches all documents and give them annotation whether the expiry date has passed or not?
I tried this:
today = timezone.now.date()
Document.objects.annotate(
expired=Value(
F('expiry_date')<today,
BooleanField()
)
)
But it raises an error: TypeError: unorderable types: F() < datetime.date()
How can I compare value from F() expression with the date?
Also, I'd like to avoid SQL and .extra()
There's no need to do that in the database. Put it in a model method:
class Document(models.Model):
expiry_date = models.DateField()
def expired(self):
return self.expiry_date < timezone.now.date()
You can use a conditional annotation.
Tested with Django 1.11.10.
from django.db.models import BooleanField, Case, When
from django.utils import timezone
Document.objects.annotate(
expired=Case(
When(expiry_date__lt=timezone.now(), then=True),
default=False,
output_field=BooleanField()
)
).order_by('expired')
This works for Django >= 2, didn't check for previous versions
from django.db import models
from django.db.models import ExpressionWrapper, Q
from django.db.models.functions import Now
Document.objects.annotate(
expired=ExpressionWrapper(Q(expiry_date__lt=Now()),
output_field=models.BooleanField())
)
source:
https://stackoverflow.com/a/57114224/11193405