2011-05-25 37 views
2

如何在接下來的X天中獲得生日的人的查詢集?我看到了this的答案,但它不適合我,因爲只有在當前出生年份才能獲得人。在接下來的X天中有生日的人的查詢集

+0

不知道Django的,但你會不會只是尋找相當於'生日<=(今天+ X天)'? – 2011-05-25 18:18:22

回答

7

假設像this--

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

模型中的下一個步驟是創建一個查詢篩選出生日在(now.month,now.day)和(then.month,then.day)之間具有一個月和一天的任何記錄。您可以實際使用queryset API訪問datetime對象的month和day屬性,方法是傳遞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) 

調試完成後,這應該返回一個查詢集,每個生日的月份和日期等於指定的元組列表中的任何月份或日期。

+0

謝謝,我認爲這個解決方案是迄今所有提交的最正確的。 – User 2011-05-25 21:08:37

+0

我唯一要做的就是用'timezone.now()'替換'datetime.now()'。 – 2016-01-17 19:20:06

0

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

類似的東西。

+0

這將導致只有一天,我需要一個介於今天和X天之間的範圍。 – User 2011-05-25 19:46:33

1

假設它的時間字段做這樣的事情(使用future_date從dimosaur答案):

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

這會讓人們只能看到當前的出生年份。 – User 2011-05-25 21:04:32

1

我能想到的2種方式,而不使用自定義查詢,均與「問題」

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)單查詢1個查詢,但需要模型改變。您需要添加bday_month和bday_day整數字段。這些顯然可以從實際日期自動填充。

這個例子的限制是你只能檢查2個月,開始月份和結束月份。設置29天你可以跳過去二月,只顯示1月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) 
) 
+0

查看django.db.models.Q,您可以使用它將大量查詢參數組合到一個查詢中。如果您「或」與Q一起編輯了多個約束條件,那麼您的第一個解決方案將適用於單個查詢。 – twneale 2011-05-25 20:19:49

0

我試圖做一個非常愚蠢的辦法,但是似乎它的工作原理:

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

我想它可以轉向昂貴的數據庫查詢:D – dmrz 2011-05-25 20:43:16

0

我對這裏的所有答覆都不滿意。它們都是「在一個範圍內檢查一個日期/一年...」的變體,從而形成了一個漫長而醜陋的查詢。這裏有一個簡單的解決方案,如果有人願意非規範化一點:

改變你的模型,而不是隻有datetime birthdate(yyyy, mm, dd)持有真正的日期,你添加一個datetime birthday(DUMMY_YEAR, mm, dd)列。因此,數據庫中的每個人都將保存其真實的出生日期,然後再與其他人共享一個固定年份的另一個出生日期。但是,不要向用戶顯示第二個字段,也不允許他們編輯它。

一旦你編輯你的模型,確保birthdatebirthday總是在你的類擴展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()是什麼,看在Complex lookups with Q objects