2010-11-19 9 views
1

Ich verwende , um Treffer auf meine Datenbankobjekte zu tally. Ich möchte die Treffer nach Objekt zählen, um zu bestimmen, welches Objekt die meisten Treffer in einem bestimmten Zeitraum hat. Die App hat zwei Modelle von Interesse hier:Kommentieren Sie in Django mit generischen Beziehungen

class Hit(models.Model): 
    created   = models.DateTimeField(editable=False) 
    ip    = models.CharField(max_length=40, editable=False) 
    session   = models.CharField(max_length=40, editable=False) 
    user_agent  = models.CharField(max_length=255, editable=False) 
    user   = models.ForeignKey(User,null=True, editable=False) 
    hitcount  = models.ForeignKey(HitCount, editable=False) 

class HitCount(models.Model): 
    hits   = models.PositiveIntegerField(default=0) 
    modified  = models.DateTimeField(default=datetime.datetime.utcnow) 
    content_type = models.ForeignKey(ContentType, 
         verbose_name="content cype", 
         related_name="content_type_set_for_%(class)s",) 
    object_pk  = models.TextField('object ID') 
    content_object = generic.GenericForeignKey('content_type', 'object_pk') 

„Hit“ Protokolle jeweils mit einem Zeitstempel zu schlagen, während HitCount speichert Gesamtanzahl der Treffer. Um die Hits von Objekt zu erhalten und innerhalb eines Zeitbereichs, muss ich folgendes tun:

Nach Datum filtern Hit erstellt Count Anzahl der Treffer pro content_object (innerhalb Zeitbereich gefiltert oben) um durch Zählung berechnet über return content_object und count

Das könnte sehr teuer sein, also plane ich einmal pro Tag calcing/caching.

Als ersten Schritt wollte ich die Anzahl der Treffer pro content_object unabhängig vom Zeitbereich zählen.

limited_hc = Hit.objects.all().values('hitcount__content_object').annotate(count = Count('hitcount__object_pk')) 

ich sofort in ein Problem laufen:

nicht Stichwort lösen können 'hitcount__content_object' in das Feld. Zur Auswahl stehen: created, hitcount, id, ip, session, user, user_agent

Nach etwas graben fand ich das annotation and generic relations do not work well together. Wenn ich object_pk anstelle von content_object verwende, funktioniert es gut, aber ich habe nicht den Namen des Objekts.

Also meine Frage: Was ist eine Alternative, um das gleiche Ergebnis zu erzielen? Wie kann man nach Objekten gruppieren, aber auch den Namen behalten?

Ich habe das Modell (content_type) und die ID (object_pk), also könnte ich diese immer separat ziehen, aber das scheint unelegant. . .

+0

Keine Möglichkeit, dies jetzt zu versuchen, aber haben Sie versucht, 'limited_hc = Hit.objects.all(). Values ​​(' hitcount__content_type ',' hitcount__object_pk '). Annotieren (count = Count (' hitcount__object_pk ')) '? –

+0

Das funktioniert, aber ich bekomme keine Verbindung zum eigentlichen Objekt. Also ich kann den Namen nicht bekommen, nur die PK. Ich müsste dann die Datenbank erneut anpingen, um die Namensdaten aus der jeweiligen Tabelle zu holen –

Antwort

1

Es wird wahrscheinlich effizienter sein, für Ihre Zwecke eine generische Beziehung zu Hit Modell hinzuzufügen:

class Hit(models.Model): 
    ... 
    object_id = models.PositiveIntegerField() 
    content_type = models.ForeignKey(ContentType) 
    content_object = generic.GenericForeignKey('content_type', 'object_id') 

und führen Sie count() Abfrage auf dem direkt Hit:

t = ContentType.objects.get_for_model(the_object_being_hit) 
id = the_object_being_hit.id 
count = Hit.objects.filter(
        created__range=(from_timestamp, to_timestamp), 
        content_type = t, 
        object_id = id 
       ).count() 

Sie können Verwenden Sie das Django South-Migrationssystem, um das Modell des Trefferzählers zu ändern. Sie können auch versuchen, Hit nach Affe-Patching seiner Meta-Klasse zu untergliedern, oder definieren Sie einfach Ihre eigenen Modelle, die Ihren Bedürfnissen besser entsprechen.

bearbeiten Wenn Sie für eine ganze Klasse Treffer zählen von Objekten oder mehrere Klassen, dann können Sie haben:

count = Hit.objects.filter(
        created__range = myrange, 
        content_type__in = set_of_types 
        ).count() 

Wo die set_of_types entweder eine Liste mit get_for_model Anrufe oder eine Abfrage erstellt werden kann Satz erhalten durch direkte Filterung der ContentType Tabelle.

Der nette Teil der count() Methode ist, dass es das Zählen in der Datenbank ermöglicht, was viel schneller ist.

Um eine Aufschlüsselung nach content_type versucht diese:

counts = Hit.objects.filter(
        created__range = myrange 
       ).values(
        'content_type' 
       ).annotate(
        Count('content_type') 
       ) 

, dass ein Wörterbuch von Zählungen vs Inhaltstyp-ID zurückgeben sollte, ganz in der Nähe zu dem, was Sie wollen.

+0

Ich arbeitete an dieser Lösung. Ich bin mir nicht sicher, ob es mir aber was ich will. Statt der Trefferanzahl eines Objekts (bei einem Zeitbereich, content_type und object_id) möchte ich die Trefferanzahl aller Objekte (mit einem Zeitbereich und einem content_type) angeben, damit ich sie bewerten kann. Es scheint, dass ich, selbst wenn ich die models.py wie beschrieben umstrukturiere, Annotate und content_object nicht gleichzeitig verwenden kann. –

+0

habe meinen Beitrag aktualisiert. Nun, ich würde in diesem Fall einfach keine Annotate verwenden, weil Ihre Abfrage zu viel Arbeit machen würde und ich denke, dass das für große Sets ziemlich langsam ist. IMO der faule Ansatz beim Zählen könnte in diesem Fall besser funktionieren. – Evgeny

+0

Vielen Dank für Ihre Nachricht. Das erreicht immer noch nicht mein Ziel. Damit bekomme ich immer noch eine Nummer. Ich hoffe auf eine iterierbare Liste. Nehmen wir an, in Ihrem letzten Codeblock würden wir, wenn wir die Zahl .count() weglassen, 5 Objekte erhalten: 3 von einem Typ A und 2 von Typ B. Ich möchte, dass die Abfrage mir 2 Objekte und die Zähle für jeden: Typ A (Anzahl = 3) und Typ B (Anzahl = 2). Nicht einfach 5 (was ich aus dem obigen Code bekomme). –

Verwandte Themen