Consulta de personas con cumpleaños en los próximos X días.

¿Cómo obtengo consultas de personas con un cumpleaños en los próximos X días? Vi esta respuesta, pero no me conviene, porque solo recibe personas con el año de nacimiento actual.

Asumiendo un modelo como este–

 class Person(models.Model): name = models.CharField(max_length=40) birthday = models.DateTimeField() # their next birthday 

El siguiente paso sería crear una consulta que filtre los registros con cumpleaños que tengan un mes y un día entre (ahora.month, now.day) y (then.month, then.day). Puede acceder a los atributos de mes y día del objeto datetime utilizando la API queryset al pasar Person.objects.filter un argumento de palabra clave como este: “birthday__month”. Intenté esto con un método de API de queryset real como “birthday__month__gte” y, sin embargo, falló. Por lo tanto, sugiero que simplemente genere una lista literal de tuplas de mes / día que representan cada (mes, día) en el rango de fechas para el que desea registros, luego compóndalas todas en una consulta con django.db.models.Q, así:

 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) 

Después de la depuración, esto debería devolver una consulta con cada persona que tenga un cumpleaños con mes y día igual a cualquiera de los meses o días en la lista especificada de tuplas.

Asumiendo que es el campo de fecha y hora, haga algo como esto (usando future_date from dimosaur answer):

 Profile.objects.get( Q(birthday__lte=future_date), Q(birthday__gte=datetime.date.today()) ) 

Puedo pensar en 2 formas sin utilizar consultas personalizadas, ambas con “problemas”

1) No eficiente, ya que hace 1 consulta por día.

 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) Consulta única, pero requiere un cambio de modelo. Necesitaría agregar los campos enteros bday_month y bday_day. Obviamente, estos se pueden rellenar automáticamente a partir de la fecha real.

La limitación de este ejemplo es que solo puede verificar contra 2 meses, el mes de inicio y el mes de finalización. Al establecer 29 días, se puede saltar en febrero, mostrando solo el 31 de enero y el 1 de marzo.

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

Si X es una constante que sabes:

 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 ) 

Algo como eso.

He intentado hacerlo de una manera realmente tonta, pero parece que funciona:

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

Estaba insatisfecho con todas las respuestas aquí. Todos son una variante de “verificar una fecha / año por una en un rango …”, lo que hace que las consultas sean largas y feas. Aquí hay una solución simple, si uno está dispuesto a desnormalizar un poco:

Cambie su modelo para que en lugar de la datetime birthdate(yyyy, mm, dd) contiene la fecha real, agregue una columna datetime birthday(DUMMY_YEAR, mm, dd) . Por lo tanto, cada persona en su base de datos habrá guardado su fecha de nacimiento real, y luego otra fecha de nacimiento con un año fijo, compartida con todos los demás. Sin embargo, no muestre este segundo campo a los usuarios y no les permita editarlo.

Una vez que haya editado su modelo, asegúrese de que la birthdate y el birthday estén siempre conectados extendiendo los models.Model Método de guardado de models.Model en su clase:

 def save(self, *args, **kwargs): self.birthday = datetime.date(BIRTHDAY_YEAR, self.birthdate.month, self.birthdate.day) super(YOUR_CLASS, self).save(*args, **kwargs) 

Y una vez que se aseguró de que cada vez que se guarda una fecha como fecha de nacimiento, el cumpleaños también se actualice, puede filtrarla con solo birthday__gte / birthday__lte . Vea un extracto de mi filtro de administración, donde cuido el límite de un año:

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

En caso de que se pregunte qué es el Q() , busque búsquedas complejas con objetos Q