Django annotate by adding months to date field - django

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()
)
)

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.

In Django, filter a query set for two datetimes being within one day

I have a Django model like:
from django.db import models
class Person(models.Model):
# ...
last_login = models.DateTimeField(null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
I want to find all Persons where last_login is within one day of date_created.
So far I have:
from datetime import timedelta
from django.db.models import F
Person.objects.annotate(
duration=F("last_login") - F("date_created")
).filter(duration__lte=timedelta(days=1))
But the tail end of the error when I do that is like:
File ".../lib/python3.7/site-packages/django/db/models/fields/__init__.py", line 1316, in to_python
parsed = parse_datetime(value)
File ".../lib/python3.7/site-packages/django/utils/dateparse.py", line 107, in parse_datetime
match = datetime_re.match(value)
TypeError: expected string or bytes-like object
I'm not sure what I'm doing wrong, probably with that timedelta(days=1).
Also (and I don't think this is the immediate error), I'm not sure if that query will take account of the fact that last_login could be None.
You can filter with:
from datetime import timedelta
from django.db.models import F
Person.objects.filter(
last_login__lte=F('date_created') + timedelta(days=1)
)
you can return records if last_login is None/NULL as well, with:
from datetime import timedelta
from django.db.models import F, Q
Person.objects.filter(
Q(last_login__lte=F('date_created') + timedelta(days=1)) |
Q(last_login=None)
)

Merge Date Time in Django ORM

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.

Group by Month and Gender in Django queryset

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')),
)

Django, query_set annotation with conditional expression

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