2013-05-11 16 views
6

Mam modelu Django, który wygląda mniej więcej tak:Django porównać wartości z dwóch obiektów

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) 

Dla każdego obiektu Response, istnieją dwa kodowania obiektów z QID = „ryzyko”, po jednym dla kodera 3 oraz jednego dla kodera 4. To, co chciałbym móc zrobić, to uzyskać listę wszystkich obiektów odpowiedzi, dla których różnica wartości między koderem 3 a koderem 4 jest większa niż 1. Pole wartości przechowuje cyfry 1-7.

Z perspektywy czasu zdałem sobie sprawę, że określenie wartości jako CharField mogło być błędem, ale mam nadzieję, że uda mi się to obejść.

wierzę coś następującym SQL zrobi co szukam, ale wolałbym zrobić to z 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

Chyba obejmować 'c1.response_id = c2.response_id' w klauzuli WHERE zapytania. –

+0

@AryehLeibTaurog tak, zrobiłem. Dzięki. – Ryan

Odpowiedz

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

Po dołączeniu do stołu już w zapytaniu ORM przydzieli drugiemu alias, w tym przypadku T3, którego możesz użyć w parametrach do extra(). Aby dowiedzieć się, co to jest alias, możesz wpaść do powłoki i print qset.query.

dokumentacja

See Django na F objects i extra

Aktualizacja: Wydaje się faktycznie nie trzeba używać extra(), lub dowiedzieć się, co alias django zastosowań, ponieważ za każdym razem odnieść się do response__coding w twoich wyszukiwań, django użyje utworzonego początkowo aliasu. Oto jeden ze sposobów, aby spojrzeć na różnice w obu kierunkach:

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')] 
dokumentacji

See Django na Q objects

BTW Jeśli będzie chciał obie instancje kodowanie, masz + 1 zapytań problemu N tutaj, ponieważ django na select_related() nie otrzyma odwrotnych relacji FK. Ale ponieważ masz już dane w zapytaniu, możesz pobrać wymagane informacje za pomocą aliasu T3, jak opisano powyżej i extra(select={'other_value':'T3.value'}). Dane z odpowiedniego rekordu kodowania będą dostępne jako atrybut w pobranej instancji kodowania, tj. Jako c.other_value.

Nawiasem mówiąc, Twoje pytanie jest dość ogólny, ale wygląda na to, że masz schematu podmiot atrybut-wartość, która w scenariuszu RDB jest powszechnie uważany anty-wzór. Może być lepiej długoterminowych (a ta kwerenda byłoby prostsze) z polem risk:

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

To jest świetne. Czy istnieje sposób, aby to działało niezależnie od tego, czy koder 3, czy koder 4 jest wyższy? – Ryan

+2

Zaktualizowałem odpowiedź za pomocą rozwiązania, które działa w obu przypadkach. –