查询未来X天内有生日的人的集合



如何获取未来X天内有生日的查询集?我看到了这个答案,但它不适合我,因为只有出生年份的人才能得到。

假设一个这样的模型——

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

下一步是创建一个查询,过滤掉生日介于(now.month,now.day)和(then.month,then.day)之间的任何记录。通过传递Person.objects.filter这样的关键字参数,您可以使用queryset API访问日期时间对象的月份和日期属性:"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)

调试后,它应该返回一个查询集,其中每个人的生日月份和日期等于指定元组列表中的任何月份或日期。

假设它是datetime字段,则执行如下操作(使用来自dimosaur答案的future_date):

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

我可以想出两种不使用自定义查询的方法,都有"问题"

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天,你可以跳过二月,只显示1月31日和3月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)
)

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

类似的东西。

我试着用一种非常愚蠢的方式来做这件事,但似乎很有效:

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

我对这里的所有回复都不满意。它们都是"在一个范围内逐个检查一个日期/年份…"的变体,进行了漫长而丑陋的查询。这里有一个简单的解决方案,如果你愿意去规范化一点:

更改您的模型,以便添加datetime birthday(DUMMY_YEAR, mm, dd)列,而不仅仅是datetime birthdate(yyyy, mm, dd)保存实际日期。因此,数据库中的每个人都会保存其真实出生日期,然后保存另一个具有固定年份的出生日期,并与其他人共享。不过,不要向用户显示第二个字段,也不允许他们编辑它

编辑模型后,通过扩展类中的models.Model保存方法:,确保birthdatebirthday始终连接

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对象的复杂查找

相关内容

  • 没有找到相关文章