2013-05-11 13 views
6

Ich habe ein Modell Django, die etwa wie folgt aussieht:Django vergleichen Werte von zwei Objekten

class Response(models.Model): 
    transcript = models.TextField(null=True) 

class Coding(models.Model): 
    qid = models.CharField(max_length = 30) 
    value = models.CharField(max_length = 200) 
    response = models.ForeignKey(Response) 
    coder = models.ForeignKey(User) 

Objekt Für jede Antwort gibt es zwei Codierungs Objekte mit qid = „Risiko“, ein für Codierer 3 und einem für Coder 4. Ich würde gerne eine Liste aller Response-Objekte erhalten, deren Wertdifferenz zwischen Coder 3 und Coder 4 größer als 1 ist. Im Wertfeld sind die Zahlen 1-7 gespeichert.

Im Nachhinein erkenne ich, dass das Einrichten eines Werts als CharField möglicherweise ein Fehler war, aber hoffentlich kann ich das umgehen.

Ich glaube, so etwas wie die folgende SQL tun würde, was ich suche, aber ich würde lieber dies mit dem ORM

SELECT UNIQUE c1.response_id FROM coding c1, coding c2 
WHERE c1.coder_id = 3 AND 
     c2.coder_id = 4 AND 
     c1.qid = "risk" AND 
     c2.qid = "risk" AND 
     c1.response_id = c2.response_id AND 
     c1.value - c2.value > 1 
+1

Ich glaube, Sie 'c1.response_id = c2.response_id' in der WHERE-Klausel der Abfrage enthalten sein sollten. –

+0

@AryehLeibTaurog ja ich tat. Vielen Dank. – Ryan

Antwort

2
from django.db.models import F 
qset = Coding.objects.filter(response__coding__value__gt=F('value') + 1, 
          qid='risk', coder=4 
        ).extra(where=['T3.qid = %s', 'T3.coder_id = %s'], 
          params=['risk', 3]) 
responses = [c.response for c in qset.select_related('response')] 

Wenn Sie eine Tabelle bereits in der Abfrage beitreten , das ORM weist dem zweiten einen Alias ​​zu, in diesem Fall T3, den Sie in den Parametern extra() verwenden können. Um herauszufinden, was der Alias ​​ist, können Sie in die Shell und print qset.query fallen.

Siehe Django Dokumentation auf F objects und extra

Update: Es scheint, dass Sie eigentlich nicht extra() verwenden müssen, oder herauszufinden, was alias Anwendungen django, denn jedes Mal, wenn Sie auf response__coding in Ihrem Lookups beziehen, Django verwendet den ursprünglich erstellten Alias. Hier ist eine Möglichkeit für die Unterschiede in beiden Richtungen zu suchen:

from django.db.models import Q, F 
gt = Q(response__coding__value__gt=F('value') + 1) 
lt = Q(response__coding__value__lt=F('value') - 1) 
match = Q(response__coding__qid='risk', response__coding__coder=4) 
qset = Coding.objects.filter(match & (gt | lt), qid='risk', coder=3) 
responses = [c.response for c in qset.select_related('response')] 

Siehe Django Dokumentation auf Q objects

BTW, Wenn Sie beide Coding Instanzen gehen zu wollen, haben Sie eine N + 1-Abfragen Problem hier, weil djangos select_related() wird keine umgekehrten FK-Beziehungen erhalten. Aber da Sie die Daten bereits in der Abfrage haben, können Sie die erforderlichen Informationen unter Verwendung des T3-Alias ​​wie oben beschrieben und extra(select={'other_value':'T3.value'}) abrufen. Die value Daten von dem entsprechenden Coding-Datensatz wären als ein Attribut auf die abgerufene Codierungsinstanz zugreifbar, d. H. Als c.other_value.

Übrigens, Ihre Frage ist allgemein genug, aber es sieht so aus, als hätten Sie ein Entity-Attribut-Wert-Schema, das in einem RDB-Szenario allgemein als Anti-Pattern betrachtet wird. Sie könnten besser langfristige (und diese Abfrage wäre einfacher) mit einem risk Feld:

class Coding(models.Model): 
    response = models.ForeignKey(Response) 
    coder = models.ForeignKey(User) 
    risk = models.IntegerField() 
    # other fields for other qid 'attribute' names... 
+0

Das ist großartig. Gibt es eine Möglichkeit, es zum Laufen zu bringen, egal ob Coder 3 oder Coder 4 höher ist? – Ryan

+2

Ich aktualisierte die Antwort mit einer Lösung, die für beide Fälle funktioniert. –