Django比较两个对象的值

时间:2013-05-11 20:17:06

标签: django django-orm django-filter

我有一个看起来像这样的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 =“risk”,一个用于编码器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 个答案:

答案 0 :(得分: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()的参数中使用该别名。要找出别名是什么,可以放入shell并print qset.query

请参阅F objectsextra

上的Django文档

更新:看起来您实际上不必使用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')]

请参阅Q objects

上的Django文档 顺便说一下,如果您想要两个编码实例,那么这里有一个N + 1查询问题,因为django的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...