Django ORM 使用 'IN' 语句过滤多个字段

Django ORM filter multiple fields using 'IN' statement

所以我在 Django 中有以下模型:

class MemberLoyalty(models.Model):
    date_time = models.DateField(primary_key=True)
    member = models.ForeignKey(Member, models.DO_NOTHING)
    loyalty_value = models.IntegerField()

我的目标是让所有元组按最近日期的成员分组。有很多方法可以做到这一点,其中之一是使用一个子查询,该子查询按最大 date_time 的成员分组,并使用其结果过滤 member_loyalty。此解决方案的工作 sql 如下:

SELECT 
    *
FROM
    member_loyalty
WHERE
    (date_time , member_id) IN (SELECT 
            max(date_time), member_id
        FROM
            member_loyalty
        GROUP BY member_id);

另一种方法是加入子查询。

我如何在 Django 查询中翻译它?我找不到使用 IN 过滤两个字段的方法,也找不到使用特定 ON 语句连接子查询的方法。

我试过:

cls.objects.values('member_id', 'loyalty_value').annotate(latest_date=Max('date_time'))

但它开始按 loyalty_value 分组。

也尝试构建子查询,但找不到如何加入它或在过滤器上使用它:

subquery = cls.objects.values('member_id').annotate(max_date=Max('date_time'))

此外,我正在使用 Mysql,所以我无法使用 .distinct('param') 方法。

这是典型的greatest-per-group query. Stack-overflow even has a tag

我认为使用最新版本的 Django 最有效的方法是通过 window query。沿线的东西应该可以解决问题。

    MemberLoyalty.objects.all().annotate(my_max=Window(
        expression=Max('date_time'),
        partition_by=F('member')
    )).filter(my_max=F('date_time'))

Update:这实际上是行不通的,因为Window注解不是filterable。我认为为了过滤 window 注释,你需要将它包装在 Subquery 中,但是对于 Subquery 你实际上没有义务使用 Window 函数,有另一种方法,这是我的下一个例子。

如果 MySQL 或 Django 不支持 window 查询,则 Subquery 发挥作用。

MemberLoyalty.objects.filter(
    date_time=Subquery(
        (MemberLoyalty.objects
            .filter(member=OuterRef('member'))
            .values('member')
            .annotate(max_date=Max('date_time'))
            .values('max_date')[:1]
        )
    )
)

如果事件 Subqueries 不可用(Django 1.11 之前)那么这也应该有效:

MemberLoyalty.objects.annotate(
    max_date=Max('member__memberloyalty_set__date_time')
).filter(max_date=F('date_time'))