【问题标题】:Django compare values of two objectsDjango比较两个对象的值
【发布时间】:2013-05-06 06:21:12
【问题描述】:

我有一个看起来像这样的 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" 的编码对象,一个用于 coder 3,一个用于 coder 4。我想要做的是获取所有 Response 对象的列表coder 3 和 coder 4 的 value 差值大于 1。 value 字段存储数字 1-7。

事后我意识到将 value 设置为 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

【问题讨论】:

  • 我认为您的意思是在查询的 WHERE 子句中包含 c1.response_id = c2.response_id
  • @AryehLeibTaurog 是的,我做到了。谢谢。

标签: django django-orm django-filter


【解决方案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()。要找出别名是什么,您可以放入 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 文档

顺便说一句,如果您想要两个 Coding 实例,那么这里会有 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...

【讨论】:

  • 这很棒。不管 coder 3 还是 coder 4 更高,有没有办法让它工作?
  • 我用适用于这两种情况的解决方案更新了答案。
猜你喜欢
  • 1970-01-01
  • 2021-07-10
  • 2013-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-02
相关资源
最近更新 更多