2010-02-24 42 views
2

我有2個模型,用戶(django.contrib.auth.models.User)和一個名爲Log的模型。兩者都包含一個「電子郵件」字段。日誌沒有指向用戶模型的ForeignKey。我試圖找出如何使用電子郵件字段作爲通用性在這兩個表上執行JOIN。在不相關的Django模型上執行SQL JOIN?

基本上有2個我想要執行的查詢。一個基本的過濾連接

#Get all the User objects that have related Log objects with the level parameter set to 3. 
User.objects.filter(log__level=3) 

我也想做一些聚合。

User.objects.all().anotate(Count('log')) 

當然,能夠做相反的做法也是很好的。

log = Log.objects.get(pk=3) 
log.user... 

有沒有辦法用ORM做到這一點?也許我可以添加到模型的Meta類來「激活」關係?

謝謝!

回答

2

您可以添加一個額外的方法到User類,使用的Monkeypatching/DuckPunching:

def logs(user): 
    return Log.objects.filter(email=user.email) 

from django.contrib.auth.models import User 
User.logs = property(logs) 

現在,你可以查詢用戶,並要求附上日誌(例如,在視圖中):

user = request.user 
logs = user.logs 

這種類型的過程在Ruby世界中很常見,但似乎在Python中被忽視。

(我整個DuckPunching項附帶的其他日子正是基於鴨子類型,在這裏我們不關心什麼類的東西是:如果它叫起來像鴨子,那麼它是一隻鴨子,只要我們如果它在衝擊時不嘎嘎,那麼繼續衝刺直到它嘎嘎)。

+0

這與註釋相結合,應該涵蓋你的兩個用例,我想。 – 2010-02-25 09:27:13

+0

+1僅供DuckPunching使用 – Wogan 2010-02-26 02:13:20

+0

因此,要「使用level參數設置爲3來獲取所有具有相關Log對象的User對象」,您應該爲每個用戶調用日誌功能。似乎很多疑問。 Extra允許在一個查詢中執行此操作。 – sunprophit 2014-01-29 22:11:17

2

爲什麼不使用extra()

例子(未測試):

User.objects.extra(
    select={ 
     'log_count': 'SELECT COUNT(*) FROM myapp_log WHERE myapp_log.email = auth_user.email' 
    }, 
) 

用於這裏的User.objects.filter(log__level=3)部分是具有額外(未測試)的當量:

User.objects.extra(
    select={ 
     'log_level_3_count': 'SELECT COUNT(*) FROM myapp_log WHERE (myapp_log.email = auth_user.email) AND (myapp_log.level=3)' 
    }, 
).filter(log_level_3_count__gt=0) 
+0

這適用於anotate用例。但不是我提到的其他用戶案例。我試圖找到一種方法來使用ORM中的單個查詢(如果可能,我寧願不要使用原始SQL)。 – erikcw 2010-02-25 01:16:17

+0

爲什麼只限於一個查詢?看看用單獨的查詢做到現在是否足夠快,什麼時候不用再查看優化。 – 2010-02-25 09:28:15

+0

你可以根據你添加的東西進行過濾() – Jiaaro 2010-02-26 01:49:22

0

不要的Log.email值總是對應於一個用戶?如果是這樣,那麼只需將一個ForeignKey(用戶)添加到日誌對象?

class Log(models.Model): 
    # ... 
    user = models.ForeignKey(User) 

隨着FK到用戶,就成了相當直截了當地找到你想要的東西:

User.objects.filter(log__level=3) 
User.objects.all().anotate(Count('log')) 

user.log_set.all() 
user.log_set.count() 

log.user 

如果Log.email值並不一定屬於你可以嘗試添加一個用戶方法到model manager

class LogManager(models.Manager): 
    def for_user(self, user): 
     return super(LobManager, self).get_query_set().filter(email=user.email) 

class Log(models.Model): 
    # ... 
    objects = LogManager() 

,然後用它是這樣的:

user = User.objects.get(pk=1) 
logs_for_user = Log.objects.for_user(user) 
+0

日誌實際上是第三方應用程序的一部分。因此添加一個新的領域並不理想。 – erikcw 2010-02-25 01:16:57