2010-05-14 40 views
5

多表繼承我有一個基地LoggedEvent模型和一些子類車型的喜歡如下:因爲他們做了相關行動標註了在Django

class LoggedEvent(models.Model): 
    user = models.ForeignKey(User, blank=True, null=True) 
    timestamp = models.DateTimeField(auto_now_add=True) 

class AuthEvent(LoggedEvent): 
    good = models.BooleanField() 
    username = models.CharField(max_length=12) 

class LDAPSearchEvent(LoggedEvent): 
    type = models.CharField(max_length=12) 
    query = models.CharField(max_length=24) 

class PRISearchEvent(LoggedEvent): 
    type = models.CharField(max_length=12) 
    query = models.CharField(max_length=24) 

用戶生成這些事件。我正在嘗試生成一份使用情況報告,說明每個用戶在上個月造成的每種事件類型的數量。我正在用Django的ORM掙扎,而當我靠近時,我遇到了一個問題。下面是查詢代碼:

def usage(request): 
    # Calculate date range 
    today = datetime.date.today() 
    month_start = datetime.date(year=today.year, month=today.month - 1, day=1) 
    month_end = datetime.date(year=today.year, month=today.month, day=1) - datetime.timedelta(days=1) 

    # Search for how many LDAP events were generated per user, last month 
    baseusage = User.objects.filter(loggedevent__timestamp__gte=month_start, loggedevent__timestamp__lte=month_end) 
    ldapusage = baseusage.exclude(loggedevent__ldapsearchevent__id__lt=1).annotate(count=Count('loggedevent__pk')) 
    authusage = baseusage.exclude(loggedevent__authevent__id__lt=1).annotate(count=Count('loggedevent__pk')) 

    return render_to_response('usage.html', { 
     'ldapusage' : ldapusage, 
     'authusage' : authusage, 
    }, context_instance=RequestContext(request)) 

兩個ldapusage和authusage都是用戶的列表,每個用戶有這應該代表用戶產生多少特定事件.Count之間的屬性註釋。但是,在兩個列表中,.count屬性都是相同的值。事實上,註釋的「計數」等於用戶生成的事件數量,無論類型如何。所以它似乎是我的具體

authusage = baseusage.exclude(loggedevent__authevent__id__lt=1) 

不是按子類排除。我試過id__lt = 1,id__isnull = True等。 HALP。

回答

4

Django模型繼承的關鍵是記住,使用非抽象基類一切實際上是基類的一個實例,它可能碰巧在單獨的表中存在一些額外的數據。這意味着,當您在基表上執行搜索時,將返回基類的實例,並且無法在子類表上執行重複的數據庫查詢以查看它們是否包含具有匹配鍵的記錄( 「我有一個事件,它是否在AuthEvent中有記錄?不。LDAP事件怎麼樣?」)。除此之外,這意味着您無法在基類的普通查詢中輕鬆過濾它們,而無需在每個子類表上進行連接。

您有幾個選擇:一個可以簡單地對子類進行查詢並計算結果(ldap_event_count = LDAPEvent.objects.filter(user=foo).count(),...),這對單個報表來說可能就足夠了。我通常建議增加一個內容類型字段的基類,所以你可以有效地判斷哪些特定的子類的實例,而無需做另一個查詢:

content_type = models.ForeignKey("contenttypes.ContentType")

這使得兩大改進:最常見的是您可以一般處理許多事件,而無需執行諸如訪問子類特定訪問器(例如event.autheventevent.ldapevent)和處理DoesNotExist之類的操作。在這種情況下,如果您的邏輯變得更加複雜(「事件是身份驗證或LDAP和...」),您也可以重寫查詢,因爲您只需要執行類似Event.objects.aggregate(Count("content_type"))這樣的操作即可獲取報告值。