2010-05-14 18 views
5

Ich habe eine Basis LoggedEvent Modell und eine Reihe von Unterklasse-Modelle wie folgt:Beschriften über Multi-Table Vererbung in 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) 

Benutzer diese Ereignisse erzeugen, wie sie die entsprechenden Aktionen ausführen. Ich versuche, einen Verwendungsbericht zu erstellen, wie viele von jedem Ereignistyp jeder Benutzer im letzten Monat verursacht hat. Ich kämpfe mit Djangos ORM und während ich in der Nähe bin, stoße ich auf ein Problem. Hier ist die Abfrage-Code:

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

Beide ldapusage und authusage sind sowohl eine Liste der Benutzer, jeder Benutzer mit einem .count Attribut kommentierten, die wie viele besondere Ereignisse darstellen soll, der Benutzer erzeugt. In beiden Listen haben die .count-Attribute jedoch den gleichen Wert. Tatsächlich ist die annotierte "Anzahl" gleich wie viele Ereignisse, die der Benutzer generiert hat, unabhängig vom Typ. So scheint es, dass meine spezifische

authusage = baseusage.exclude(loggedevent__authevent__id__lt=1) 

nicht von Unterklasse ausgeschlossen ist. Ich habe versucht, id__lt = 1, id__isnull = True, und andere. Halbe.

Antwort

4

Der Schlüssel zur Django-Modellvererbung ist, dass mit einer nicht abstrakten Basisklasse alles wirklich eine Instanz der Basisklasse ist, die möglicherweise zusätzliche Daten auf der Seite aus einer separaten Tabelle geschnallt hat. Dies bedeutet, dass Sie bei der Suche nach Basistabellen Instanzen der Basisklasse zurückerhalten. Es gibt keine Möglichkeit, die Unterklasse zu ermitteln, ohne die Datenbankabfragen in den Unterklassentabellen wiederholen zu müssen, um festzustellen, ob sie einen Datensatz mit einem übereinstimmenden Schlüssel enthalten. "Ich habe ein Ereignis. Hat es einen Eintrag in AuthEvent? Nein. Was ist mit LDAP-Ereignis? ..."). Dies bedeutet unter anderem, dass Sie sie in normalen Abfragen der Basisklasse nicht einfach filtern können, ohne eine Verknüpfung für jede Unterklasse zu erstellen.

Sie haben ein paar Möglichkeiten: man würde einfach Ihre Abfragen für die Unterklasse durchführen und die Ergebnisse (ldap_event_count = LDAPEvent.objects.filter(user=foo).count(), ...) zusammenfassen, was für einen einzelnen Bericht ausreichen könnte. Ich empfehle in der Regel einen Inhaltstyp Feld auf die Basisklasse hinzugefügt, so dass Sie effizient, welche bestimmte Unterklasse eine Instanz, ohne sagen, ist, kann eine weitere Abfrage zu tun:

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

, dass zwei wesentliche Verbesserungen ermöglicht: die häufigste ist dass Sie mit vielen Events generisch umgehen können, ohne etwas tun zu müssen, wie zum Beispiel die Unterklassen-spezifischen Accessoren (zB event.authevent oder event.ldapevent) und DoesNotExist. In diesem Fall würde es auch trivial sein, Ihre Abfrage neu zu schreiben, da Sie einfach so etwas wie Event.objects.aggregate(Count("content_type")) machen könnten, um die Berichtswerte zu erhalten, was besonders praktisch ist, wenn Ihre Logik komplizierter wird ("Ereignis ist Auth oder LDAP und ...").