2013-05-11 14 views
6

Ho un modello di Django che sembra qualcosa di simile:Django confrontare i valori di due oggetti

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) 

Per ogni oggetto Response, ci sono due oggetti di codifica con qid = "rischio", uno per il codificatore 3 ed uno per coder 4. Quello che vorrei essere in grado di fare è ottenere un elenco di tutti gli oggetti Response per i quali la differenza di valore tra coder 3 e coder 4 è maggiore di 1. Il campo value memorizza i numeri 1-7.

Mi rendo conto a posteriori che impostare un valore come un CharField potrebbe essere stato un errore, ma spero di poterlo aggirare.

credo che qualcosa di simile a SQL avrebbe fatto quello che sto cercando, ma io preferirei fare questo con l'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

Credo che si intende includere 'c1.response_id = c2.response_id' nella clausola WHERE della query. –

+0

@AryehLeibTaurog sì, l'ho fatto. Grazie. – Ryan

risposta

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

Quando si partecipa a una tabella già nella query , l'ORM assegnerà al secondo un alias, in questo caso T3, che è possibile utilizzare in parametri a extra(). Per scoprire qual è l'alias, è possibile inserire nella shell e print qset.query.

documentazione

Vedere Django su F objects e extra

Aggiornamento: Sembra che in realtà non c'è bisogno di usare extra(), o capire che cosa alias Django usi, perché ogni volta che si fa riferimento a response__coding nelle vostre ricerche, Django userà l'alias creato inizialmente. Ecco un modo per cercare le differenze in entrambe le direzioni:

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

Vedere Django su Q objects

A proposito, se avete intenzione di voler entrambe le istanze di codifica, si dispone di un N problema + 1 query qui, perché django's select_related() non otterrà relazioni FK inverse. Ma dal momento che hai già i dati nella query, puoi recuperare le informazioni richieste usando l'alias T3 come descritto sopra e extra(select={'other_value':'T3.value'}). I dati value dal record di codifica corrispondente sono accessibili come attributo nell'istanza di codifica recuperata, ad esempio c.other_value.

Per inciso, la tua domanda è abbastanza generale, ma sembra che tu abbia uno schema di entità-attributo-valore, che in uno scenario RDB è generalmente considerato un anti-modello. Si potrebbe essere meglio a lungo termine (e questa query sarebbe più semplice) con un campo risk:

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

Questo è fantastico. C'è un modo per farlo funzionare indipendentemente dal fatto che coder 3 o coder 4 sia più alto? – Ryan

+2

Ho aggiornato la risposta con una soluzione che funziona per entrambi i casi. –