2016-04-03 50 views
0

我經常發現自己不止一次在我的Django應用程序中編寫了相同的條件。我通常會將它封裝在一個返回Django Q()對象的函數中,這樣我就可以在一個地方維護標準。從其他Q()對象構建Django Q()對象,但有關係跨越上下文

我會做這樣的事情在我的代碼:

def CurrentAgentAgreementCriteria(useraccountid): 
    '''Returns Q that finds agent agreements that gives the useraccountid account current delegated permissions.''' 
    AgentAccountMatch = Q(agent__account__id=useraccountid) 
    StartBeforeNow = Q(start__lte=timezone.now()) 
    EndAfterNow = Q(end__gte=timezone.now()) 
    NoEnd = Q(end=None) 
    # Now put the criteria together 
    AgentAgreementCriteria = AgentAccountMatch & StartBeforeNow & (NoEnd | EndAfterNow) 
    return AgentAgreementCriteria 

這使得它,這樣我就不用想通過DB模式不止一次,我可以從這些組合的返回值功能建立更復雜的標準。到目前爲止效果很好,並且在數據庫模型更改時節省了我的時間。

當我開始將這些函數的標準結合起來時,我已經意識到Q()對象固有地與被調用的對象類型.filter()綁定在一起。這是我所期望的。

我偶爾會發現自己想從我的一個函數中使用Q()對象來構造另一個Q對象,該對象旨在過濾不同但相關的模型實例

讓我們用一個簡單/人爲的例子來展示我的意思。 (這很簡單,通常這是不值得的開銷,但請記住,我在這裏使用一個簡單的例子來說明什麼是我的應用程序更復雜。)

說我有一個函數返回一個Q( )對象,找到所有Django的用戶,其用戶名與「A」開頭:

def UsernameStartsWithAaccount(): 
    return Q(username__startswith='a') 

說,我有一個相關的模型,該模型與設置,包括他們是否想要從我們的電子郵件用戶配置文件:

class UserProfile(models.Model): 
    account = models.OneToOneField(User, unique=True, related_name='azendalesappprofile') 
    emailMe = models.BooleanField(default=False) 

說我想查找所有具有au的UserProfiles以'a'開頭的服務名稱,並希望用它們發送一些電子郵件通訊。我可以很容易地編寫用於後者的Q()對象:

wantsEmails = Q(emailMe=True) 

但發現自己想要的東西,爲前者做這樣的事情:

startsWithA = Q(account=UsernameStartsWithAaccount()) 
# And then 
UserProfile.objects.filter(startsWithA & wantsEmails) 

不幸的是,不工作(它會在我嘗試它時生成無效的PSQL語法)。

換句話說,我正在尋找一條沿Q(account=Q(id=9))的語法,它將返回與Q(account__id=9)相同的結果。

所以,有幾個問題,從這個出現:

  1. 是否有使用Django Q上的語法()對象,可以讓你的「上下文」添加到他們,讓他們從模型交叉關係的界限,你正在運行.filter()?
  2. 如果不是,這在邏輯上是可能的嗎? (因爲我可以寫Q(account__id=9)當我想要做像Q(account=Q(id=9))這樣的東西時好像是)。

回答

1

也許有人建議更好的東西,但我最終手動將上下文傳遞給這些函數。我不認爲有一個簡單的解決方案,因爲您可能需要調用整個相關錶鏈來到您的領域,如table1__table2__table3__profile__user__username,您會如何猜測?用戶表也可以鏈接到table2,但在這種情況下您不需要它,所以我認爲您無法避免手動設置路徑。

此外,您可以將字典傳遞到Q()和列表或字典到filter()函數,比使用關鍵字參數和應用&更容易。

def UsernameStartsWithAaccount(context=''): 
    field = 'username__startswith' 
    if context: 
     field = context + '__' + field 
    return Q(**{field: 'a'}) 

然後,如果你只需要AND你的條件,你可以將它們組合成一個列表,並通過過濾:

UserProfile.objects.filter(*[startsWithA, wantsEmails]) 
+0

我其實更喜歡'&'和'|'經營者表達標準。如果您使用描述性名稱命名較小的Q()變量,則幾乎可以大聲朗讀代碼並使其在英語中有意義。但它看起來像你必須使用字典'**'語法來創建字段名稱,就像你建議的那樣,我將採用你的解決方案的那部分。 (謝謝!) – Azendale