django group query by day with timezone specification - django

I have a model with datetime field saved in UTC:
class Order(models.Model):
created = models.DateTimeField(db_index=True, auto_now_add=True)
...
I need to count number of orders per day for last 30 days to represent it with plot.
time_zone = 'Europe/Kiev'
date_to = datetime.date.today().strftime("%Y-%m-%d") + ' 23:59:59'
date_from = (datetime.date.today() - datetime.timedelta(days=29)).strftime("%Y-%m-%d") + ' 00:00:00'
date_range = (date_from, date_to)
data = Order.objects.filter(
created__range=date_range
).extra(
{'date': "date(created) AT TIME ZONE '{0}'".format(time_zone)}
).values('date').annotate(items=Count('id'))
But this call still groups data by day in UTC which is wrong. I can simply check that by counting number of orders in specific day:
data = Order.objects.filter(
created_range=('2016-05-03 00:00:00', '2016-05-03 23:59:59')
).count()
Is there any way to make database group items with timezone correctly in one call, because looping through 30 days and making 30 calls is slow?
Working with django 1.8.7 and postgres 9.4.4.1

As it turned out, there were some mistakes in extra({...}).
So
extra({'date': "date(created) AT TIME ZONE '{0}'".format(time_zone)}
Should be changed to
extra(select={'date': "date(goods_order.created AT TIME ZONE '{0}')".format(time_zone))
where goods_order.created is database_tablename.fieldname

Related

Django ORM query on fk set

I have a problem with lookup that looks for a value in related set.
class Room(models.Model):
name = models.Charfield(max_lentgh=64)
class Availability(models.Model):
date = models.DateField()
closed = models.BooleanField(default=False)
room = models.ForeignKey(Room)
Considering that there is one availability for every date in a year. How can use ORM query to find whether room withing given daterange (i.e. 7 days) has:
availability exist for every day within given daterange
none of the availabilities has closed=True
I was unable to find any orm examples that check whether all objects within daterange exist
You can enumerate over the dates, and ensure that it has for that date an Availability with closed=False:
from datetime import date, timedelta
rooms = Room.objects.all()
start_date = date(2022, 7, 21) # first day
for dd in range(7): # number of days
dt = start_date + timedelta(days=dd)
rooms = rooms.filter(availability__date=dt, availability__closed=False)
The rooms will after the for loop have a QuerySet with all Rooms that have for all dates in that range Availability objects with closed=False.

Group by weeks, months, days

I would like to group querysets by a date interval according to a datetime attribute of the model. I want to show average values from the model on a day-to-day, week-by-week and month-by-month basis.
E.g.
Week commencing 01/01/2017 - average distance: 30
Week commencing 08/01/2017 - average distance: 40
...
Can this be achieved with the standard Django queryset API?
Assuming the following model which might match your description
class Activity(models.Model):
timestamp = models.DateTimeField(auto_now_add=True, blank=True)
distance = models.IntegerField()
You can achieve a week by week statistic with the following query
from django.db.models.functions import ExtractWeek, ExtractYear
from django.db.models import Sum, Count
stats = (Activity.objects
.annotate(year=ExtractYear('timestamp'))
.annotate(week=ExtractWeek('timestamp'))
.values('year', 'week')
.annotate(avg_distance=Avg('distance'))
)
Sample output
<QuerySet [{'year': 2018, 'week': 31, 'distance': 3.2}]>
To recover the first day of week, check Get date from week number
In particular:
for record in stats:
week = "{year}-W{week}-1".format(year=record['year'], week=record['week'])
timestamp = datetime.datetime.strptime(week, "%Y-W%W-%w")
There are a bunch of field lookups specifically for date/datetime fields: week, day, month (should be combined with year) etc.

Django view returning count of users based on last_login

I have a Django application where I have a view that returns the count of the users who did login today. Corresponding statement is as follows:
login_count= User.objects.filter(last_login__startswith=timezone.now().date()).count()
I want to get the count of users who did login once in the last week and last the month. Like, here timezone.now() returns today's date, is there anything like range which will cover a week or a month?
Yes, there is, and its even named range.
Example
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
User.objects.filter(last_login__range=(start_date, end_date))
Of for the last week
today = datetime.date.today()
last_week = today - datetime.timedelta(days=7)
User.objects.filter(last_login__range=(last_week, today))
However,
Filtering a DateTimeField with dates won’t include items on the last day, because the bounds are interpreted as “0am on the given date”. If pub_date was a DateTimeField, the above expression would be turned into this SQL:
A quick solution would be to simply add a day, so the date range includes up to the last minute of today, but not more (also update last_week).
today = datetime.date.today() + datetime.timedelta(days=1)
last_week = datetime.date.today() - datetime.timedelta(days=7)
User.objects.filter(last_login__range=(last_week, today))

Django query datetime by adding 5 hours 30 minutes to the original column

I'm trying to write a Django query for orders that when added 5 hours 30 minutes satisfy the condition,created_at is datetime
SELECT id, created_at + time '5:30' as created_at , date(created_at + time '5:30') as created_date
FROM orders WHERE date(created_at + time '5:30') > today_date
How to convert this into Django ORM ?
I assume that TIME_ZONE in setting is set to 'Asia/Kolkata' (i.e. UTC+5:30). If you have this setting, you can use django's timezone module.
Let's say the model name in 'Order'. The above query will be like this in ORM form:
from django.utils import timezone
orders = Order.objects.filter(created_at__gt=timezone.now().date())
And to convert the date got from this query into your timezone:
order = orders.first()
created_at = timezone.localtime(order.created)

django model filter date time from midnight

I used django model filter with date.
Origin data has time of Africa/Abidjan(UTC+00:00) and I save this data on my database.
I set timezone about Asia/Seoul(UTC+09:00)and it's save well in my database.
But I filter data with time_range=[2015-11-15, 2015-11-16], I got data about from 15 o'clock to 14 o'clock. I want to get data from 0 o'clock to 23 o'clock.
How can I get this? I do this url, but it doesn't work well.
class Post(models.Model):
created_time = models.DateTimeField()
If I have a model like post, and I get date from user.
input_date = '2015-11-16' (from user)
from_date = datetime.strptime(input_date, '%Y-%m-%d').date()
to_date = from_date + datetime.timedelta(days=1)
posts = Post.objects.filter(created_time__range=[from_date, to_date])
I used upper code and got data from 2015-11-15 15:00:00 to 2015-11-16 14:00:00.
Try using from_date = datetime.strptime(input_date, '%Y-%m-%d') (without .date()), maybe it requires datetime instead of date object.