通过注解的值进行约束过滤的高级ORM用法

时间:2019-08-07 12:40:51

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

我具有以下已构造的查询:

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}

老鹰眼中的您可能会注意到,最后两个记录在'user'和'survey'键上是伪重复的,而在其他任何一个上都没有。

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

预期结果:

{'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()

1 个答案:

答案 0 :(得分:0)

您已经明显拥有了想要的结果,因此您也可以在python中完成此操作,这几乎是您所建议的:

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

尝试失败的原因是:您应该使用代表调查和用户的唯一密钥。使用and,您将得到result['user'],因为如果a and b都为b,则True将返回latest_score

但是,我相信您的ORM查询有问题。您需要确保获得特定用户给出的created_date,而不是给出分数的最后一天获得的分数之一(例如,您有两个得分不同的TruncDate(不同时间)但是在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_score

participants$scope.$apply(function () { $scope.lists = res.lists; }); 并没有什么意义,因为分数只是用户最后一次为特定调查提供分数的那一行;参与者总是1。

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