Django ORM: django aggregate over filtered reverse relation



这个问题与Django ORM远程相关:通过进一步限制生成的查询集,根据相关模型的时间顺序字段过滤主模型。

模型

假设我们有以下模型:

class Patient(models.Model)
    name = models.CharField()
    # other fields following
class MedicalFile(model.Model)
    patient = models.ForeignKey(Patient, related_name='files')
    issuing_date = models.DateField()
    expiring_date = models.DateField()
    diagnostic = models.CharField()

查询

我需要选择在指定日期有效的所有文件,很可能是过去的文件。我在这里遇到的问题是,对于每个患者,都会有一个小的重叠期,患者将有 2 个有效文件。如果我们要查询这个小时间范围内的日期,我只需要选择最新的文件。

更重要的是:考虑病人John Doe。 他将拥有从 2012 年开始的"不间断"文件字符串,如下所示:

+---+------------+-------------+
|ID |issuing_date|expiring_date|
+---+------------+-------------+
|1  |2012-03-06  |2013-03-06   |
+---+------------+-------------+
|2  |2013-03-04  |2014-03-04   |
+---+------------+-------------+
|3  |2014-03-04  |2015-03-04   |
+---+------------+-------------+

正如人们很容易观察到的那样,这些文件的有效性有几天的重叠。例如,在 2013-03-05 中,文件 1 和 2 有效,但我们只考虑文件 2(作为最新的文件)。我猜用例并不特殊:这是管理订阅的情况,为了获得连续订阅,您将提前续订订阅。

现在,在我的应用程序中,我需要查询历史数据,例如,给我所有在 2013-03-05 有效的文件,只考虑"最新"的文件。我能够通过使用 RawSQL 解决这个问题,但我希望有一个没有原始 SQL 的解决方案。在上一个问题中,我们能够通过反向关系的聚合来过滤"最新"文件,如下所示:

qs = MedicalFile.objects.annotate(latest_file_date=Max('patient__files__issuing_date'))
qs = qs.filter(issuing_date=F('latest_file_date')).select_related('patient')

问题是我们需要通过过滤 2013-03-05 来限制计算latest_file_date的范围。但是聚合函数不会在过滤的查询集上运行...

"差"的解决方案

我目前正在通过一个额外的查询集子句(用您的具体应用程序替换"app")来执行此操作:

reference_date = datetime.date(year=2013, month=3, day=5)
annotation_latest_issuing_date = {
    'latest_issuing_date': RawSQL('SELECT max(file.issuing_date) '
                                  'FROM <app>_medicalfile file '
                                  'WHERE file.person_id = <app>_medicalfile.person_id '
                                  '  AND file.issuing_date <= %s', (reference_date, ))
}
qs = MedicalFile.objects.filter(expiring_date__gt=reference_date, issuing_date__lte=reference_date)
qs = qs.extra(**annotation_latest_issuing_date).filter(issuing_date=F('latest_issuing_date'))

这样写入时,查询集返回正确的记录数。

问题:如果没有 RaWSQL 和(已经暗示的)具有相同的性能水平,如何实现它?

您可以使用

id__in并提供嵌套的筛选查询集(就像在给定日期有效的所有文件一样)。

qs = MedicalFile.objects
.filter(id__in=self.filter(expiring_date__gt=reference_date, issuing_date__lte=reference_date))
.order_by('patient__pk', '-issuing_date')
.distinct('patient__pk')  # field_name parameter only supported by Postgres

order_by按患者对文件进行分组,最晚的签发日期排在最前面。 然后,distinct检索每个患者的第一个文件。但是,结合order_bydistinct时需要一般护理:https://docs.djangoproject.com/en/1.9/ref/models/querysets/#django.db.models.query.QuerySet.distinct

编辑:从第一个过滤器中删除了单个患者的依赖性,并将latest更改为order_bydistinct的组合

考虑p是一个 Patient 类实例。

我认为你可以做这样的事情:

p.files.filter(issue_date__lt='some_date', expiring_date__gt='some_date')

请参阅 https://docs.djangoproject.com/en/1.9/topics/db/queries/#backwards-related-objects

或者也许使用Q魔术查询对象...

最新更新