Django - 从每个组的最新日期检索对象 - PersonPhoto

Posted

技术标签:

【中文标题】Django - 从每个组的最新日期检索对象 - PersonPhoto【英文标题】:Django - retrieve objects from latest date for each group - PersonPhoto 【发布时间】:2021-11-06 21:31:38 【问题描述】:

我的数据库包含不同人的护照图像。比如:

class Person(models.Model):
    pass

class PersonPhoto(models.Model):
    date_captured = models.DateField()
    person = models.ForeignKey(Person, null=False)

我想为每个人提取他最近拍摄日期的所有图像。 因此,如果 A 人拥有 8 月 5 日、5 日、9 日、11 日、11 日的照片,而 B 人拥有 8 月 7 日、9 日、13 日、13 日、19 日、19 日的照片,那么我想为 A 人获取 8 月 11 日的两张照片,以及 B 人 8 月 19 日的两张照片。

我目前的做法是这样的:

specific_dates_queryset = Q()
for photo in PersonPhoto.objects.all().values('person_id').annotate(max_date=Max('date_captured')):
    specific_dates_queryset |= Q(person_id=photo["person_id"], date_captured=photo["max_date"])


for photo in PersonPhoto.objects.filter(specific_dates_queryset).order_by("person_id"):
    print(f"image for person photo.person_id, of date photo.date_captured")

这个想法是首先找到每个人的照片的最新日期,然后在一个新的查询中从这些日期获取这些人的这些图像。

是否有更简单的解决方案可以在数据库中完成所有操作并避免冗余查询和数据提取?

【问题讨论】:

【参考方案1】:

在单个查询中执行此操作的一种简单方法是用相关人员的最新日期注释每张照片,然后按注释过滤。这应该在查询集中返回所有需要的PersonPhoto

from django.db.models import Max, F

PersonPhoto.objects.annotate(
   latest=Max('person__personphoto__date_captured')
).filter(
    date_captured=F('latest')
)

由于注释,我不确定这将如何表现,这可能取决于您使用的数据库和数据的性质

【讨论】:

【参考方案2】:

您可以预取一个人的所有相关照片,并根据捕捉到的人的最新日期过滤这些照片,如下所示:

from django.db.models import F, Max, Prefetch

person_qs = Person.objects.annotate(
    latest_photo_date=Max('personphoto__date_captured')
).prefetch_related(
    Prefetch(
        'personphoto_set',
        queryset=PersonPhoto.objects.annotate(
            person_latest_photo_captured=Max('person__personphoto__date_captured')
        ).filter(
            date_captured=F('person_latest_photo_captured')
        ),
        to_attr='latest_photos',
    )
)

所有最新的PersonPhoto 实例都将作为Person 实例的latest_photos 属性中的列表提供,因此您可以像这样访问它们:

for person in person_qs:
    print(f'Latest images for person.name taken on person.latest_photo_date:')
    for photo in person.latest_photos:
        print(f'Photo ID: photo.id - Captured at: photo.date_captured')
    print()

输出:

Latest images for B taken on 2021-08-19:
Photo ID: 10 - Captured at: 2021-08-19
Photo ID: 11 - Captured at: 2021-08-19

Latest images for A taken on 2021-08-11:
Photo ID: 5 - Captured at: 2021-08-11
Photo ID: 4 - Captured at: 2021-08-11

这将总共执行两个查询,一个用于人员列表,另一个用于获取每个人的所有过滤后的相关照片。

【讨论】:

以上是关于Django - 从每个组的最新日期检索对象 - PersonPhoto的主要内容,如果未能解决你的问题,请参考以下文章

如何从对象集合中获取第二个最新日期的对象

根据最大日期获取每组的最新行

Django 以 5 个成员组的形式检索数据

Django ORM group by,并找到每个组的最新项目(窗口函数)

Django 获取组的最新外国计数

从分组的 MySQL 数据中获取最新日期