sql - 使用 Django ORM 中的先前对象注释查询集

标签 sql django-queryset django-orm

示例模型:

class User(models.Model):
    pass


class UserStatusChange(models.Model):
    user = models.ForeignKey(User, related_name='status_changes')
    status = models.CharField()
    start_date = models.DateField()

我想使用 end_date 字段注释 UserStatusChanges 查询集,并且 end_date 应等于下一个的 start_date同一用户的状态更改。

最终,我希望能够做到这一点:

qs = UserStatusChange.ojects.annotate(end_date=???)
qs = qs.filter(start_date__lte=some_date, end_date__gte=another_date)

从逻辑上讲,注释应该是这样的:

qs.annotate(
    end_date=qs.filter(
        user=OuterRef('user'),
        start_date__gt=OuterRef('start_date')
    ).order_by('start_date').first().start_date)

但如果可能的话,它应该是一个数据库查询。

解决方案:

subquery = UserStatusChange.objects.filter(user=OuterRef('user'),
                                           start_date__gt=OuterRef('start_date')).order_by('start_date')
UserStatusChange.objects.annotate(end_date=Subquery(subquery.values('start_date')[:1]))

这有效,感谢@hynekcer 的回答。但是使用aggregate我得到了错误:

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.

UPD:在 Django 2.0+ 中可以用 Lead Window function 来解决。 在 SQL 中,它会是这样的:

select 
     user_id, status_id, start_date,
     LEAD(start_date, 1) over (partition by user_id order by start_date)
from user_status_change;

最佳答案

您可以使用Subquery()在 Django 1.11 中使用 OuterRef()。

from django.db.models import Min, OuterRef, Subquery
from django.db.models.functions import Coalesce

default_end = now()  # or the end of the recorded history
qs = (
    UserStatusChanges.objects
    .annotate(
        end_date=Coalesce(
            Subquery(
                UserStatusChanges.objects
                .filter(
                    user=OuterRef('user'),
                    start_date__gt=OuterRef('start_date')
                )
                .order_by()
                .aggregate(Min('start_date'))
            ),
            default_end
        )
    )
)
qs = qs.order_by('user', 'start_date')
# an optional filter
qs = qs.filter(start_date__lte=some_date, end_date__gte=another_date, user__in=[...])

它在执行时被编译为一个查询,例如当与 prefetch_lated 的用户过滤器结合使用时。如果您希望最后一项也有一个有意义的 end_date,那么您可以使用 Coalesce()默认值等于当前时间戳。

关于sql - 使用 Django ORM 中的先前对象注释查询集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47018481/

相关文章:

Django 反向查找链接查询集

python - 如何从 Django 中的 RawQuerySet 中检索值?

django - 如何使用现有字段之一的相同值但不同数据类型来注释 Django 查询集?

django - Django ORM:Group by和Max

sql - PostgreSQL:在单个 SQL 语法中按从文本列计算的数值排序

c# - 新查询返回的架构与基本查询不同 (C#/SQL - VS 2012)

django - 在 Django 中缓存模型实例(对象级缓存)的最佳框架/可重用应用程序是什么?

python - Django IntegerField 返回字符串(!) - 如何强制转换为 int?

数据库迁移后 MySQL 事件未运行

mysql - Oracle SQL 查询 : Finding the number of counts of similar objects between parent project and child project