Опрос людей с днем ​​рождения в ближайшие X дней - PullRequest
2 голосов
/ 25 мая 2011

как мне получить набор запросов людей с днем ​​рождения в следующие X дней? Я видел этот ответ, но он меня не устраивает, потому что получает людей только с текущим годом рождения.

Ответы [ 6 ]

6 голосов
/ 26 мая 2011

Предположим, что модель такая -

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

Следующим шагом будет создание запроса, отфильтровывающего любые записи с днями рождения, имеющими месяц и день между (now.month, now.day) и (then.month, then.day). На самом деле вы можете получить доступ к атрибутам месяца и дня объекта datetime, используя API набора запросов, передав Person.objects.filter аргумент ключевого слова, например: «birthday__month». Я попробовал это с помощью фактического метода API набора запросов, например "birthday__month__gte", и это не удалось. Поэтому я бы предложил просто создать буквальный список кортежей месяц / день, представляющих каждый (месяц, день) в диапазоне дат, для которого вы хотите записи, а затем объединить их все в запрос с django.db.models.Q, например так:

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)

После отладки должен возвращаться набор запросов с каждым человеком, у которого день рождения с месяцем и днем, равным любому из месяцев или дней в указанном списке кортежей.

1 голос
/ 26 мая 2011

Я могу придумать 2 способа без использования пользовательских запросов, оба с «проблемами»

1) Неэффективно, так как выполняет 1 запрос в день

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) Один запрос, но требуется изменение модели. Вам необходимо добавить целые поля bday_month и bday_day. Очевидно, что они могут быть заполнены автоматически с реальной даты.

Ограничением этого примера является то, что вы можете проверять только 2 месяца, месяц начала и конец месяца. Установив 29 дней, вы можете перепрыгнуть через февраль, показывая только 31 января и 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)
)
1 голос
/ 26 мая 2011

Предполагая, что это поле даты и времени, сделайте что-то вроде этого (используя future_date из ответа димозавра):

Profile.objects.get(
    Q(birthday__lte=future_date),
    Q(birthday__gte=datetime.date.today())
)
0 голосов
/ 26 июля 2015

Я был недоволен всеми ответами здесь.Все они представляют собой вариант «проверять одну дату / год по одному в диапазоне ...», делая длинные, уродливые запросы.Вот простое решение, если кто-то захочет немного денормализовать:

Измените свою модель, чтобы вместо datetime birthdate(yyyy, mm, dd) с реальной датой вы добавили столбец datetime birthday(DUMMY_YEAR, mm, dd).Таким образом, каждый человек в вашей базе данных сохранит свою реальную дату рождения, а затем еще одну дату рождения с фиксированным годом, которая будет сообщена всем остальным.Однако не показывайте это второе поле пользователям и не позволяйте им редактировать его.

После того, как вы отредактировали свою модель, убедитесь, что birthdate и birthday всегда связаны, расширяя models.Model метод сохранения в вашем классе:

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

И как только вы убедитесь, что при сохранении даты в качестве даты рождения, день рождения также обновляется, вы можете отфильтровать ее с помощью birthday__gte / birthday__lte.См. Отрывок из моего фильтра администратора, в котором я позабочусь о границе года:

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

Если вам интересно, что такое Q(), посмотрите на Сложные поиски с объектами Q

0 голосов
/ 26 мая 2011

Я пытался сделать это очень глупо, но, кажется, это работает:

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))
0 голосов
/ 25 мая 2011

Если X - постоянная, которую вы знаете:

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
)

Нечто подобное.

...