Queryset of people with a birthday in the next X days

前端 未结 6 1870
春和景丽
春和景丽 2021-01-03 13:29

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.

6条回答
  •  情书的邮戳
    2021-01-03 13:59

    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

提交回复
热议问题