This is a pretty straight-forward question:
I have two models, each with a DateField. I want to query Model-A based on the date in Model-B. I want a query that returns all the objects of Model-A that have a date within 2 years, plus or minus, of the date in one object of Model-B. How can this be done?
Assuming you have a date value from model B, calculate two dates: one - 2 years in the past and another - 2 years in the future by the help of python-dateutil module (taken partially from here). Then, use __range notation to filter out A records by date range:
from dateutil.relativedelta import relativedelta
def yearsago(from_date, years):
return from_date - relativedelta(years=years)
b_date = b.my_date
date_min, date_max = yearsago(b_date, 2), yearsago(b_date, -2)
data = A.objects.filter(my_date__range=(date_min, date_max))
where b is a B model instance.
Also see: Django database query: How to filter objects by date range?
Hope that helps.
Related
In a Django query, how would you filter by a timestamp's week within a month?
There's a built-in week accessor, but that refers to week-of-the-year, e.g. 1-52. As far as I can tell, there's no other built-in option.
The only way I see to do this is to calculate the start and end date range for the week, and then filter on that using the conventional means.
So I'm using a function like:
def week_of_month_date(year, month, week):
"""
Returns the date of the first day in the week of the given date's month,
where Monday is the first day of the week.
e.g. week_of_month_date(year=2022, month=8, week=2) -> date(2022, 8, 7)
"""
assert 1 <= week <= 5
assert 1 <= month <= 12
for i in range(1, 32):
dt = date(year, month, i)
_week = week_of_month(dt)
if _week == week:
return dt
and then to calculate for, say, the 3rd week of July, 2022, I'd do:
start_date = week_of_month_date(2022, 7, 3)
end_date = week_of_month_date(2022, 7, 3) + timedelta(days=7)
qs = MyModel.objects.filter(created__gte=start_date, created__lte=end_date)
Is there an easier or more efficient way to do this with the Django ORM or SQL?
The easiest way to do this using datetime objects is to quite simply subtract the current date weekly year value, with the yearly week value for the 1st day (or 1st week) of the month.
You can use the .isocalendar() function to achieve this:
dt.isocalendar[1] - dt.replace(day=1).isocalendar()[1] + 1
Basically if the week is 46 and that means the first week is week 44 then the resulting output should be 2.
UPDATE
I misunderstood the question, the answer is clear below. However, you may want to consider revising your function based on my above comments.
Come to think of it, if you have a datetime object, you can get the isocalendar week and filter using that like so:
MyModel.objects.filter(created__week=dt.isocalendar()[1])
dt.isocalendar() returns essentially a tuple of 3 integers, [0], is the year, [1], is the iso week (1-52 or 53) and [2], the day of the week (1-7).
As per the docs here:
https://docs.djangoproject.com/en/4.1/ref/models/querysets/#week
There is a built-in filter for isoweek out of the box :)
However, filtering by "week of month" is not possible within the realms of "out of the box".
You might consider writing your own query expression object which accepts an isocalendar object and converts that? But I think you would be better off converting a datetime object and use the isoweek filter.
There's a neat little blog post here to get you started if you really want to do that:
https://dev.to/idrisrampurawala/writing-custom-django-database-functions-4dmb
I am trying to build a function in Python where if a user provides an offset frequency such as 1D 10M 1Y then I can provide the date using the offset.
Example if user inputs 1M along with a date 2021-08-25
pd.Timestamp('2021-08-25') - pd.tseries.frequencies.to_offset('1M')
The above code outputs Timestamp('2021-07-31 00:00:00') which is not one month prior to the date provided by user. Expected Output Timestamp('2021-07-25 00:00:00')
How can I achieve this?
You need to use pd.DateOffset:
>>> pd.Timestamp("2020-08-25") - pd.DateOffset(months=1)
Timestamp('2020-07-25 00:00:00')
The frequencies.to_offset() returns a <MonthEnd> object, hence why you were getting 07-31:
>>> pd.tseries.frequencies.to_offset("1M")
<MonthEnd>
Since years and months don't have a fixed frequency you can use the pd.offsets.DateOffset method to deal with calendar additions of years and months, similar to the implementation of relativedelta.
Because you'll need to specify both the argument names and values for this to work, you can change your function to pass a dict with the arguments as opposed to just the offset frequency.
import pandas as pd
def offset_date(timestamp, offset):
"""
offset: dict of {'offset frequency': periods}
"""
return timestamp + pd.offsets.DateOffset(**offset)
timestamp = pd.Timestamp('2021-08-25')
offset_date(timestamp, {'months': 1})
#Timestamp('2021-09-25 00:00:00')
offset_date(timestamp, {'days': 10})
#Timestamp('2021-09-04 00:00:00')
offset_date(timestamp, {'years': -3})
#Timestamp('2018-08-25 00:00:00')
I am trying to use MongoEngine to apply a filter on a mongodb collection called Employees. The filter is based on country, city and join_date.
The filter condition is that the number of months obtained by subtracting join_date from today's date should be a minimum of "x" months, where x is a setting value. So, for example, if x is 18 months, I need to find all employees whose join_date was a minimum of 18 months prior to today's date.
I am trying to achieve this by calling the filter() method, but I'm unable to figure out how to do that.
matching_records = Employees.objects(
country=rule.country,
city=rule.city) \
.filter(relativedelta.relativedelta(datetime.datetime.now, join_date).months > 18)
I get an error, "name join_date is not defined". I am unable to figure out how to get the filter to work. Please help.
You need to use the lte (less than or equal) or gte (greater than or equal) operators like this:
from datetime import datetime, timedelta
import dateutil.relativedelta
from mongoengine import *
connect()
now = datetime.utcnow()
yesterday = now - dateutil.relativedelta.relativedelta(days=5)
past = now - dateutil.relativedelta.relativedelta(months=20)
class TestDate(Document):
dt = DateTimeField()
# Saving 3 objects to verify the query works
TestDate(dt=now).save()
TestDate(dt=yesterday).save()
TestDate(dt=past).save()
TestDate.objects(dt__lte=now - dateutil.relativedelta.relativedelta(months=18)) # return TestData associated with `past`
TestDate.objects(dt__gte=now - dateutil.relativedelta.relativedelta(months=18)) # return TestData associated with `now` and `yesterday`
I have a field month-year which is in datetime64[ns] format.
How do i use this field in where clause to get rolling 12 months data(past 12 months data).
Below query does not work, but I would like something that filters data for 12 months.
select * from ABCD.DEFG_TABLE where monthyear > '2019-01-01'
FYI - It is an oracle database. If i can avoid hard coding the value 2019-01-01 that would be great!!
You need to use the datetime and set the date format as below.
Just get your relative date and if you follow datetime format as YYYYMMDD, use strftime from date time with regex string as ("%Y%m%d")
import datetime
import pandas
from dateutil.relativedelta import relativedelta
query = "SELECT * FROM ng_scott.Emp"
between_first = datetime.date.today()
between_second = between_first - relativedelta(years=1)
# GET THE DATASET
dataset = pd.read_sql(query , con=engine)
# PARSE THE DATASET
filtered_dataset = dataset[(dataset['DOJ'] > between_first ) & (dataset['DOJ'] > between_second )]
print(filtered_dataset)
You can do this with pure SQL.
The following expression dynamically computes the beginning of the months 1 year ago:
add_months(trunc(sysdate, 'month'), -12)
This phrases as: take the date at the first day of the current month, and withdraw 12 months from it.
You can just use it as a filter condition:
select * from ABCD.DEFG_TABLE where monthyear > add_months(trunc(sysdate, 'month'), -12)
NB: this assumes that monthyear is of datatype date.
I'm using Django 1.5.1, Python 3.3.x, and can't use raw queries for this.
Is there a way to get a QuerySet grouped by weekday, for a QuerySet that uses a date __range filter? I'm trying to group results by weekday, for a query that ranges between any two dates (could be as much as a year apart). I know how to get rows that match a weekday, but that would require pounding the DB with 7 queries just to find out the data for each weekday.
I've been trying to figure this out for a couple hours by trying different tweaks with the __week_day filter, but nothing's working. Even Googling doesn't help, which makes me wonder if this is even possible. Any Django guru's here know how, if it is possible to do?
Since extra is deprecated, here is a new way of grouping on the day of the week using ExtractDayOfWeek.
from django.db.models.functions import ExtractWeekDay
YourObjects.objects
.annotate(weekday=ExtractWeekDay('timestamp'))
.values('weekday')
.annotate(count=Count('id'))
.values('weekday', 'count')
This will return a result like:
[{'weekday': 1, 'count': 534}, {'weekday': 2, 'count': 574},.......}
It is also important to note that 1 = Sunday and Saturday = 7
Well man I did an algorithm this one brings you all the records since the beginning of the week (Monday) until today
for example if you have a model like this in your app:
from django.db import models
class x(models.Model):
date = models.DateField()
from datetime import datetime
from myapp.models import x
start_date = datetime.date(datetime.now())
week = start_date.isocalendar()[1]
day_week =start_date.isoweekday()
days_quited = 0
less_days = day_week
while less_days != 1:
days_quited += 1
less_days -= 1
week_begin = datetime.date(datetime(start_date.year,start_date.month,start_date.day-days_quited))
records = x.objects.filter(date__range=(week_begin, datetime.date(datetime.now())))
And if you add some records in the admin with a range between June 17 (Monday) and June 22 (today) you will see all those records, and if you add more records with the date of tomorrow for example or with the date of the next Monday you will not see those records.
If you want the records of other week unntil now you only have to put this:
start_date = datetime.date(datetime(year, month, day))
records = x.objects.filter(date__range=(week_begin, datetime.date(datetime.now())))
Hope this helps! :D
You need to add an extra weekday field to the selection, then group by that in the sum or average aggregation. Note that this becomes a database specific query, because the 'extra' notation becomes passed through to the DB select statement.
Given the model:
class x(models.Model):
date = models.DateField()
value = models.FloatField()
Then, for mysql, with a mapping of the ODBC weekday to the python datetime weekday:
x.objects.extra(select={'weekday':"MOD(dayofweek(date)+5,7)"}).values('weekday').annotate(weekday_value=Avg('value'), weekday_value_std=StdDev('value'))
Note that if you do not need to convert the MySql ODBC weekday (1 = Sunday, 2 = Monday...) to python weekday (Monday is 0 and Sunday is 6), then you do not need to do the modulo.
For model like this:
class A(models.Model):
date = models.DateField()
value = models.FloatField()
You can use query:
weekday = {"w": """strftime('%%w', date)"""}
qs = A.objects.extra(select=weekday).values('w').annotate(stat = Sum("value")).order_by()