2013-05-11 5 views
6

У меня есть модель Django, которая выглядит примерно так:Джанго сравнить значения двух объектов

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) 

Для каждого объекта Response, есть два кодирование объектов с QID = «риском», один для кодера 3 и один для кодера 4. То, что я хотел бы сделать, это получить список всех объектов Response, для которых разница в значении между кодером 3 и кодером 4 больше 1. Поле значений хранит номера 1-7.

Я понимаю, что установка значения как CharField может быть ошибкой, но, надеюсь, я смогу обойти это.

Я считаю, что-то вроде следующего SQL будет делать то, что я ищу, но я предпочел бы сделать это с ОРМ

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

Я думаю, что вы имели в виду включить 'c1.response_id = c2.response_id' в ИНЕКЕ вашего запроса. –

+0

@AryehLeibTaurog да я. Благодарю. – Ryan

ответ

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

Когда вы присоединяетесь к столу уже в запросе , ORM назначит второй псевдоним, в данном случае T3, который вы можете использовать в параметрах до extra(). Чтобы узнать, что такое псевдоним, вы можете попасть в оболочку и print qset.query.

См Джанго документацию по F objects и extra

Update: Кажется, что вы на самом деле не должны использовать extra(), или выяснить, что псевдоним Джанго использует, потому что каждый раз, когда вы обратитесь к response__coding в ваших поисков, django будет использовать созданный первоначально псевдоним. Вот один из способов, чтобы искать различия в любом направлении:

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

См Джанго документации на Q objects

Кстати, если вы будете хотеть оба экземпляра Coding, то есть + 1 запросы проблема N здесь, потому что django's select_related() не получит обратных отношений FK. Но поскольку у вас уже есть данные в запросе, вы можете получить необходимую информацию, используя псевдоним T3, как описано выше, и extra(select={'other_value':'T3.value'}). Данные value из соответствующей записи кодирования будут доступны в качестве атрибута в извлеченном экземпляре кодирования, то есть как c.other_value.

Кстати, ваш вопрос достаточно общий, но похоже, что у вас есть схема объекта-атрибута-значения, которая в сценарии RDB обычно считается анти-шаблоном. Вы могли бы быть лучше в долгосрочной перспективе (и этот запрос будет проще) с risk поле:

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

Это замечательно. Есть ли способ заставить его работать независимо от того, является ли кодер 3 или кодер 4 выше? – Ryan

+2

Я обновил ответ с помощью решения, которое работает для обоих случаев. –

Смежные вопросы