【问题标题】:Django - retrieve objects from latest date for each group - PersonPhotoDjango - 从每个组的最新日期检索对象 - 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}")

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

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

【问题讨论】:

    标签: python python-3.x django django-models django-queryset


    【解决方案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
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-08
        • 2021-09-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多