【问题标题】:Advanced ORM usage for constraint filtering by an annotated value通过注释值进行约束过滤的高级 ORM 用法
【发布时间】:2019-08-07 12:40:51
【问题描述】:

我已经构建了以下查询/(ies):

users = User.objects.filter(is_active=True)

date_range = [start_date, timezone.now()]

results = SurveyResult.objects.filter(
    user__in=users,
    created_date__range=date_range,
).annotate(
    date=TruncDate('created_date'),
    total_score=Sum('score'),
    participants=Count('user'),
).values(
    'survey',
    'user',
    'date',
    'total_score',
    'participants',
).order_by(
    'date',
)

将生成的 QuerySet 中的每个结果快速打印为:

for result in results:
    print(results)

...输出数据如下:

{'survey': UUID('eb51368e-994a-4c0b-8d8a-e00ed20b5926'), 'user': UUID('25afbbfd-bddf-4fe8-bbac-758bd96093b0'), 'date': datetime.date(2019, 7, 26), 'total_score': 90, 'participants': 1}
{'survey': UUID('09947780-8d60-499f-87e3-fc53a9490960'), 'user': UUID('6afdea22-ea10-4069-9e7b-43fb6955ce0e'), 'date': datetime.date(2019, 7, 26), 'total_score': 17, 'participants': 1}
{'survey': UUID('890d0a21-6e27-457f-902e-e2f37d2fad6c'), 'user': UUID('d98684f7-97ab-49d7-be50-0cc9b6465ef5'), 'date': datetime.date(2019, 7, 26), 'total_score': 35, 'participants': 1}
{'survey': UUID('890d0a21-6e27-457f-902e-e2f37d2fad6c'), 'user': UUID('d98684f7-97ab-49d7-be50-0cc9b6465ef5'), 'date': datetime.date(2019, 7, 27), 'total_score': 62, 'participants': 1}

您中的鹰眼可能会注意到最后两条记录在“用户”和“调查”键上是伪重复的,但在其他任何一个上都没有。

我的问题是:我到底如何从这个记录集中删除记录(使用我构建的 Django ORM 查询或以标准的 Python 方式),其中“调查”和“用户”键匹配 - 只保留根据“日期”的最新记录......所以离开我:

预期结果:

{'survey': UUID('eb51368e-994a-4c0b-8d8a-e00ed20b5926'), 'user': UUID('25afbbfd-bddf-4fe8-bbac-758bd96093b0'), 'date': datetime.date(2019, 7, 26), 'total_score': 90, 'participants': 1}
{'survey': UUID('09947780-8d60-499f-87e3-fc53a9490960'), 'user': UUID('6afdea22-ea10-4069-9e7b-43fb6955ce0e'), 'date': datetime.date(2019, 7, 26), 'total_score': 17, 'participants': 1}
{'survey': UUID('890d0a21-6e27-457f-902e-e2f37d2fad6c'), 'user': UUID('d98684f7-97ab-49d7-be50-0cc9b6465ef5'), 'date': datetime.date(2019, 7, 27), 'total_score': 62, 'participants': 1}

我尝试过的事情

我在想也许可以利用这样的东西:

unique = { result['survey'] and result['user'] : result for result in results }.values()

【问题讨论】:

  • 你的数据库后端是什么?
  • @schwobaseggl Postgres
  • 如果您已经拥有所有数据,只需在 python 中执行此操作:遍历结果并丢弃除最后一个以外的所有重复分数。
  • 我试过循环数据,但它并没有完全丢弃我想要的。
  • 如果您的结果排序正确(如上所示,即所有重复记录均按日期顺序排列),那么只需创建一个新字典,其键为survey_id + user_id 的组合并将值一一分配给新字典(具有相同组合的字典会覆盖前一个字典)。然后通过删除键再次展平字典。你似乎在做什么,所以不清楚你这样做时到底出了什么问题。

标签: python django django-orm python-3.7 django-2.2


【解决方案1】:

你显然已经得到了你想要的结果,所以你也可以在 python 中这样做,这几乎是你提出的:

unique = {f"{result['survey']}+{result['user']}": result for result in results}.values()

您的尝试不起作用的原因是:您应该使用代表调查和用户的唯一键。使用and,您只会得到result['user'],因为如果两者都是Truea and b 将返回b

但是,我相信您的 ORM 查询有问题。您需要确保获得特定用户给出的latest_score,而不是在最后一天给出分数的分数之一(假设您有两个分数不同created_date(不同时间)但是,在@ 987654329@,他们有相同的date)。为了得到最后的分数,你应该试试这个:

results = SurveyResult.objects.filter(
    user__in=users,
    created_date__range=date_range,
).values(
    'survey', 'user'  # group by unique survey, user pair
).annotate(last_score=
    Window(expression=LastValue('score'),
           partition_by=[F('user'), F('survey')],
           order_by=F('created_date').asc(),
    )
).annotate(date=
    Window(expression=LastValue(TruncDate('created_date')),
           partition_by=[F('user'), F('survey')],
           order_by=F('created_date').asc(),
    )
).values(
    'survey', 'user', 'date', 'last_score'
)

total_scoreparticipants 没有意义,因为分数只是用户最后一次为特定调查评分的那一行;参与者总是 1。

如果您按调查和日期而不是调查和用户进行汇总,那将是有意义的。

【讨论】:

  • 不幸的是,不是。这不是我要找的。​​span>
  • 数据格式不正确,没有通过一次测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-25
  • 2018-11-03
  • 2012-03-20
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2020-02-12
相关资源
最近更新 更多