2017-09-05 47 views
3

我有這個運行良好的ORM查詢。我測試了以下看到輸出,並將其作爲實際預期:將靜態CASE WHEN語句轉換爲動態以提供給.annotate()

members=Members.objects.all().annotate(age_groups=Case(
       When(birthdate__year__range=(2007,2017), then=Value('0-10')), 
       When(birthdate__year__range=(1997,2006), then=Value('11-20')), 
       When(birthdate__year__range=(1987,1996), then=Value('21-30')), 
       When(birthdate__year__range=(1977,1986), then=Value('31-40')), 
       When(birthdate__year__range=(1967,1976), then=Value('41-50')), 
       When(birthdate__year__range=(1957,1966), then=Value('51-60')), 
       When(birthdate__year__range=(1947,1956), then=Value('61-70')), 
       When(birthdate__year__range=(1937,1946), then=Value('71-80')), 
       When(birthdate__year__range=(1927,1936), then=Value('81-90')), 
       When(birthdate__year__range=(1917,1926), then=Value('91-100')), 
       default=Value('100+'), output_field=CharField()) 
       ).values('age_groups',).annotate(total=Count('age_groups')) 

但我想將它移動到一個功能,使用戶可以指定要使用的日期字段,並做分組他們喜歡(如0-20,21-40等)。爲此,我編寫了下面的函數,它在調用時只輸出CASE WHEN statements作爲字符串。問題是當我通過它annotate()我得到strno resolve error

希望你得到我想要做的。我需要下面的函數的輸出傳遞到annoate()

from datetime import date 
def AgeGrouping(field_name,required_min_age=0,required_max_age=100,jump_by=10): 
now_year=date.today().year 

reply='Case(' 
just_started=True 
for i in range(now_year-required_min_age,now_year-required_max_age,-jump_by): 

    if just_started==False: 
     min_age=max_age + 1 
     max_age=min_age + 9 

    else: 
     min_age= now_year - i 
     max_age= min_age + 10 

    min_year=now_year- min_age 
    max_year= now_year - max_age 

    reply="\n".join([reply,"When(" + field_name + "__year__range=(" + str(max_year) + "," + str(min_year) + "), then=Value('" + str(min_age) + "-" + str(max_age) + "')),"]) 

    just_started=False 

reply="\n".join([reply, "default=Value('100+'), output_field=models.CharField())"]) 

return reply 

然後調用它像這樣:

print(AgeGrouping('birthdate',required_min_age=0,required_max_age=100,jump_by=10)) 

回答

2

你目前的方法是創建一個包含所需的代碼串。可以將該字符串評估爲代碼,但這可能存在安全問題。

更好的方法是使用循環創建一個When()對象的列表。然後,您可以在註釋中呼叫Case()時解開列表。

def age_grouping(field_name, required_min_age=0, required_max_age=100, jump_by=10): 
    ... 
    whens = [] 
    for i in range(now_year-required_min_age,now_year-required_max_age,-jump_by): 
     # Set min_age, max_age, min_year, max_year 
     ... 
     description = '%s-%s' % (min_age, max_age) # e.g. '0-10' 
     # create dict of kwargs for When() 
     kwargs = {'%s__year__range' % field_name: (min_year, max_year)} 
     whens.append(When(then=Value(description), **kwargs)) 

    # Return the Case() object that you can pass to annotate() 
    return Case(*whens, default=Value('100+'), output_field=CharField()) 

那麼你應該能夠調用

Members.objects.all().annotate(age_groups=age_grouping(...)) 
+0

感謝@Alasdair ......都按預期非常好。 –