Django сравнивает значения двух объектов

у меня есть 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)

для каждого объекта ответа есть два объекта кодирования с qid = "риск", один для кодера 3 и один для кодера 4. Я хотел бы иметь возможность получить список всех объектов ответа, для которых разница в значении между кодером 3 и кодером 4 больше 1. В поле Значение хранятся номера 1-7.

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

Я считаю, что что-то вроде следующего SQL сделает то, что я ищу, но я бы предпочел сделать это с 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 ответов


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.

см. документацию Django на F объектов и дополнительно

обновление: кажется, вам на самом деле не нужно использовать extra(), или выяснить, какой псевдоним использует django, потому что каждый раз, когда вы ссылаетесь на 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')]

см. документацию Django на Q объектов

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

Кстати, Ваш вопрос достаточно общий, но похоже, что у вас есть схема entity-attribute-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...