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...