how do i get queryset of people with a birthday in the next X days? I saw this answer, but it does not suit me, because gets people only with current year of birth.
Assuming a model like this--
class Person(models.Model):
name = models.CharField(max_length=40)
birthday = models.DateTimeField() # their next birthday
The next step would be to create a query filtering out any records with birthdays having a month and day in between (now.month, now.day) and (then.month, then.day). You can actually access the month and day attributes of the datetime object using the queryset API by passing Person.objects.filter a keyword argument like this: "birthday__month." I tried this with an actual queryset API method like "birthday__month__gte" and it failed though. So I would suggest simply generating a literal list of month/day tuples representing each (month, day) in the date range you want records for, then compose them all into a query with django.db.models.Q, like so:
from datetime import datetime, timedelta
import operator
from django.db.models import Q
def birthdays_within(days):
now = datetime.now()
then = now + timedelta(days)
# Build the list of month/day tuples.
monthdays = [(now.month, now.day)]
while now <= then:
monthdays.append((now.month, now.day))
now += timedelta(days=1)
# Tranform each into queryset keyword args.
monthdays = (dict(zip(("birthday__month", "birthday__day"), t))
for t in monthdays)
# Compose the djano.db.models.Q objects together for a single query.
query = reduce(operator.or_, (Q(**d) for d in monthdays))
# Run the query.
return Person.objects.filter(query)
After debugging, this should return a queryset with each person who has a birthday with month and day equal to any of the months or days in the specified list of tuples.
Assuming it's datetime field do something like this (using future_date from dimosaur answer):
Profile.objects.get(
Q(birthday__lte=future_date),
Q(birthday__gte=datetime.date.today())
)
I can think of 2 ways without using custom queries, both with "problems"
1) Not efficient as it does 1 query per day
start = datetime.date.today()
max_days = 14
days = [ start + datetime.timedelta(days=i) for i in xrange(0, max_days) ]
birthdays = []
for d in days:
for p in Profile.objects.filter(birthday__month=d.month, birthday__day=d.day):
birthdays.append(p)
print birthdays
2) Single query, but requires a model change. You would need to add bday_month and bday_day integer fields. These can obviously be populated automatically from the real date.
The limitation of this example is that you can only check against 2 months, start month and the end month. Setting 29 days you could jump over february, showing only Jan 31 and Mar 1.
from django.db.models import Q
start = datetime.date.today()
end = start + datetime.timedelta(days=14)
print Profile.objects.filter(
Q(bday_month=start.month) & Q(bday_day__gte=start.day) |
Q(bday_month=end.month) & Q(bday_day__lte=end.day)
)
If X is a constant that you know:
import datetime
future_date = datetime.date.today() + datetime.timedelta(days=X)
Profile.objects.filter(
birth_date__month=future_date.month,
birth_date__day=future_date.day
)
Something like that.
I have tried to do it in a really silly way, but seems it works:
import datetime
from django.db.models import Q
x = 5
q_args = ''
for d in range(x):
future_date = datetime.date.today() + datetime.timedelta(days=d)
q_args += 'Q(birth_date__month=%d, birth_date__day=%d)%s' % (
future_date.month,
future_date.day,
' | ' if d < x - 1 else ''
)
people = People.objects.filter(eval(q_args))
I was unsatisfied with all replies here. They are all a variant on "check one date/year by one in a range...", making a long, ugly queries. Here is a simple solution, if one is willing to denormalize a bit:
Change your model so instead of just datetime birthdate(yyyy, mm, dd) holding the real date you add a datetime birthday(DUMMY_YEAR, mm, dd) column. So every person in your DB will have saved its real birth date, and then a another birth date with a fixed year, shared with everyone else. Don't show this second field to users, though, and don't allow them to edit it.
Once you edited your model, make sure the birthdate and birthday are always connected by extending models.Model save method in your class:
def save(self, *args, **kwargs):
self.birthday = datetime.date(BIRTHDAY_YEAR,
self.birthdate.month, self.birthdate.day)
super(YOUR_CLASS, self).save(*args, **kwargs)
And once you ensured that whenever a date is saved as birthdate, the birthday is updated too, you can filter it with just birthday__gte/birthday__lte. See an excerpt from my admin filter, where I take care of a year boundary:
def queryset(self, request, queryset):
if self.value() == 'today':
# if we are looking for just today, it is simple
return queryset.filter(birthday = datetime.date(
BIRTHDAY_YEAR, now().month, now().day
))
if self.value() == 'week':
# However, if we are looking for next few days,
# we have to bear in mind what happens on the eve
# of a new year. So if the interval we are looking at
# is going over the new year, break the search into
# two with an OR.
future_date = (now() + datetime.timedelta(days=7)).date()
if (now().year == future_date.year):
return queryset.filter(
Q(birthday__gte = datetime.date(
BIRTHDAY_YEAR, now().month, now().day
)) &
Q(birthday__lte = datetime.date(
BIRTHDAY_YEAR,
future_date.month,
future_date.day)
)
)
else:
return queryset.filter(
# end of the old year
Q(birthday__gte = datetime.date(
BIRTHDAY_YEAR, now().month, now().day
)) &
Q(birthday__lte = datetime.date(BIRTHDAY_YEAR,12, 31)) |
# beginning of the new year
Q(birthday__gte = datetime.date(BIRTHDAY_YEAR, 1, 1)) &
Q(birthday__lte = datetime.date(BIRTHDAY_YEAR,
future_date.month,
future_date.day)
)
)
In case you wonder what the Q() is, look on Complex lookups with Q objects
Related
I have a model like this
class Maca(models.Model):
created_at = models.DateTimeField(
auto_now_add=True
)
Now I want in the views.py file to get all the entries that have created today,
I'm trying this
Maca.objects.filter(created_at=datetime.today().date())
But this looks for the clock that object is created too.
P.S I can't change the field in model because I need the clock too in other purposes.
Can someone help me to select all entries that have been created today?
Thanks in advance
You have to just write a valid filter like this :
from datetime import datetime
today = datetime.today()
year = today.year
month = today.month
day = today.day
meca = Meca.objects.filter(created_at__year=year,
created_at__month=month, created_at__day=day)
I think the main reason is because you trying to compare dt field with date.
You can use prefixes for a field.
Or you can compare your 'today' value as dt(just an example):
today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
records = Maca.objects.filter(created_at >= today)
there many ways to get today data from database.
year = datetime.datetime.now().strftime('%y')
month = datetime.datetime.now().strftime('%m')
day = datetime.datetime.now().strftime('%d')
filtereddata = Maca.objects.filter(created_at__year=year,created_at__month=month, created_at__day=day )
the second way is to filter with contains
filtereddata = Maca.objects.filter(created_at__contains=datetime.today().date())
if datetime.datetime not work. just remove one datetime only use single datetime
I am new to functions and I am trying to write a function that returns the number of days between two dates:
My attempt:
import datetime
from dateutil.parser import parse
def get_x_days_ago (date_from, current_date = None):
td = current_date - parse(date_from)
if current_date is None:
current_date = datetime.datetime.today()
else:
current_date = datetime.datetime.strptime(date_from, "%Y-%m-%d")
return td.days
print(get_x_days_ago(date_from="2021-04-10", current_date="2021-04-11"))
Expected outcome in days:
1
So there seem to be multiple issues, and as I said in the comments, a good idea would be to separate the parsing and the logic.
def get_x_days_ago(date_from, current_date = None):
if current_date is None:
current_date = datetime.datetime.today()
return (current_date - date_from).days
# Some other code, depending on where you are getting the dates from.
# Using the correct data types as the input to the get_x_days_ago (datetime.date in this case) will avoid
# polluting the actual logic with the parsing/formatting.
# If it's a web framework, convert to dates in the View, if it's CLI, convert in the CLI handling code
date_from = parse('April 11th 2020')
date_to = None # or parse('April 10th 2020')
days = get_x_days_ago(date_from, date_to)
print(days)
The error you get is from this line (as you should see in the traceback)
td = current_date - parse(date_from)
Since current_date="2021-04-11" (string), but date_from is parsed parse(date_from), you are trying to subtract date from the str.
P.S. If you have neither web nor cli, you can put this parsing code into def main, or any other point in code where you first get the initial strings representing the dates.
It looks like you're already aware that you can subtract a datetime from a datetime. I think, perhaps, you're really looking for this:
https://stackoverflow.com/a/23581184/2649560
I'm trying an exclude() query to exclude objects created on the same day as a user's profile. The user's user.created field is a DateTimeField, but I want to exclude all objects created on the same date as the user:
I'm trying:
my_objects = MyClass.objects.exclude(created__contains=user.created.date())
but it throws this error: Warning: Incorrect datetime value: '%2013-09-14%' for column 'created' at row 1
Is there a better way or a way to fix this?
Compare year, month, day:
d = user.created
my_objects = MyClass.objects.exclude(
created__year=d.year,
created__month=d.month,
created__day=d.day
)
or compare with the date (inclusive) and the next day (exclusive)
d = user.created.date()
my_objects = MyClass.objects.exclude(
created__gte=d,
created__lt=d + datetime.timedelta(days=1)
)
How can I subtract or add 100 years to a datetime field in the database in Django?
The date is in database, I just want to directly update the field without retrieving it out to calculate and then insert.
I would use the relativedelta function of the dateutil.relativedelta package, which will give you are more accurate 'n-years ago' calculation:
from dateutil.relativedelta import relativedelta
import datetime
years_ago = datetime.datetime.now() - relativedelta(years=5)
Then simply update the date field as others have shown here.
Use timedelta. Something like this should do the trick:
import datetime
years = 100
days_per_year = 365.24
hundred_years_later = my_object.date + datetime.timedelta(days=(years*days_per_year))
The .update() method on a Django query set allows you update all values without retrieving the object from the database. You can refer to the existing value using an F() object.
Unfortunately Python's timedelta doesn't work with years, so you'll have to work out 100 years expressed in days (it's 36524.25):
MyModel.objects.update(timestamp=F('timestamp')+timedelta(days=36524.25))
Though setting the number of days in a year as 365.25 (from (365+365+365+366)/4) perfectly offsets the difference-in-days error, it would sometimes lead to unwanted results as you might cause undesirable changes in attributes other than year, especially when you are adding/subtracting 1 or a few years.
If you want to just change the year while preventing changes in other datetime's attributes, just do the algebra on the year attribute like the following:
from datetime import datetime
d = my_obj.my_datetime_field
""" subtract 100 years. """
my_obj.my_datetime_field = datetime(d.year-100, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, d.tzinfo)
my_obj.save()
Hope it helps!
Subtract year from today and use this format.
x = datetime.datetime(2020 - 100, 5, 17)
import datetime
datetime.date(datetime.date.today().year - 100, datetime.date.today().month, datetime.date.today().day)
I Know it's an old question, but I had the problem to find out a good one to solve my problem, I have created this: Use plus(+) or minus(-) to handle with:
import datetime # Don't forget to import it
def subadd_date(date,years):
''' Subtract or add Years to a specific date by pre add + or - '''
if isinstance(date,datetime.datetime) and isinstance(years,int):
day,month,year = date.day , date.month , date.year
#If you want to have HOUR, MINUTE, SECOND
#With TIME:
# day,month,year,hour,minute,second = date.day, date.month,date.year,date.hour,date.minute,date.second
py = year + years # The Past / Futur Year
new_date_str = "%s-%s-%s" % (day,month,py) # New Complete Date
# With TIME : new_date_str = "%s-%s-%s %s:%s:%s" % (month,day,py,hour,minute,second)
try:
new_date = datetime.datetime.strptime(new_date_str,"%d-%m-%Y")
except ValueError: # day is out of range for month (February 29th)
new_date_str = "%s-%s-%s" % (1,month+1,py) # New Complete Date : March 1st
new_date = datetime.datetime.strptime(new_date_str,"%d-%m-%Y")
return new_date
# With TIME : return datetime.datetime.strptime(new_date_str,"%d-%m-%Y %H:%M:%Y")
return None
I need to make a gql query against a set of some objects which have a date field. I am very new to python and also the GAE so I am a bit igorant to this. I am looking in the documentation but cannot find quite what I am looking for. Basically I have made the following class method
Event.getEventsForMonth(cls, month,year):
So I am trying to query my object where the month and year of the date field fall in a specified range. I have tried to create a date object and use that for the comparison but I have had no joy so far.
dateto = str(year)+str(month+1)+"01"
datefrom = str(year)+str(month)+"01"
if month + 1 == 13:
dateto = str(year+1)+"01"+"01"
query = GqlQuery("SELECT * FROM Event WHERE date >= :datefrom AND date <= :dateto", dateto=dateto, datefrom=datefrom)
return query
This method to me looks awful, as I am not very up on the core methods I can use from Python inline with the GQL Query.
Any help is appreciated
Cheers,
Andrew
First, store your dates as DateProperty or DateTimeProperty instances in the datastore.
Then, you can do your query something like this:
def getEventsForMonth(self, month, year):
start_date = datetime.datetime(year, month, 1)
if month == 12:
end_date = datetime.datetime(year + 1, 1, 1)
else:
end_date = datetime.datetime(year, month + 1, 1)
return Events.all().filter('date >=', start_date).filter('date <=', end_date).fetch(1000)