Django按最后创建的对象反向查询

2 投票
1 回答
1094 浏览
提问于 2025-04-17 16:10

我有两个模型:

class SomeActivity(models.Model):
    name = models.ChartField(max_length=100)

class SomeStatus(models.Model):
    name = models.CharField(max_length=100)
    status = models.IntegerField(choises=STATUS_CHOISES)
    some_activity = models.ForeignKey(SomeActivity, related_name='statuses')

活动的最后状态就是当前状态。为了获取这个状态,我使用了以下代码:

try:
    last_status = some_activity.statuses.latest('id')
except:
    last_status = None

但是问题是,当我想查询所有Activities,并且这些活动的last_status要符合status__in=[1, 2]时,就遇到了困难。

1 个回答

2

这个解决方案有点复杂,但我觉得可以用:

from django.db.models import Max

max_status_ids = SomeActivity.objects.filter(statuses__isnull=False).annotate(
              last_status_id=Max('statuses__id')
              ).values_list('last_status_id', flat=True)
status_satisfied_ids = SomeStatus.objects.filter(id__in=list(max_status_ids),
              status__in=[1, 2]).values_list('id', flat=True)
activities = SomeActivity.objects.filter(statuses__id__in=list(
              status_satisfied_ids))

我希望有更好的解决办法。


更新

试试这个:

max_status_ids = SomeActivity.objects.annotate(last_status_id=Max('statuses')
                                    ).values('last_status_id')
activities = SomeActivity.objects.filter(statuses__in=max_status_ids,
                                         statuses__status__in=(1,2))
  1. Django在使用qsqs.values()qs.values_list()时,会自动生成子查询,所以在__in查找后不需要用list()包裹查询集(这样做还会引入不必要的计算和中间SQL),也不需要在qs.values_list()里写flat=True
  2. activities里使用statuses__id__in查找时,已经引入了表连接,所以最好把status__instatus_satisfied_ids移到activities里,这样可以利用这个连接。否则,status_satisfied_ids会引入额外的选择。

或者你可以使用PostgreSQL的窗口函数直接按排名位置进行过滤。

撰写回答