13đź‘Ť
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.
1đź‘Ť
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())
)
- [Django]-Django: related_name attribute (DatabaseError)
- [Django]-Django filters, got an unexpected keyword argument
- [Django]-How to use filter_horizontal on a ForeignKey between two apps using ContentTypes in Django admin?
1đź‘Ť
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)
)
- [Django]-Invalid BACKEND error when I have the seemingly correct settings — why?
- [Django]-Django REST framework nested serializer and POST nested JSON with files
- [Django]-Django: two sessions in the same browser
0đź‘Ť
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.
- [Django]-Uwsgi: RuntimeError: cannot release un-acquired lock
- [Django]-Import data into Django model with existing data?
0đź‘Ť
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))
- [Django]-Setting global variables with Cactus App for Mac
- [Django]-Django: after form.is_valid check, cleaned_data is missing the image field
- [Django]-Where to delete model image?
0đź‘Ť
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
- [Django]-Django paginate the original search result
- [Django]-Django List deserialization in rest-framework
- [Django]-Using django-taggit, is it possible to limit the tags to pre approved values?
- [Django]-Sending notification to one user using Channels 2
- [Django]-Django(Python) AttributeError: 'NoneType' object has no attribute 'split'