如何在单个表达式中从多对一关系的过滤结果中获取最新值注释一个Django QuerySet?
鉴于这个玩具架构:
from django.db import models
class Lorem(models.Model):
""" Lorem ipsum, dolor sit amet. """
class LoremStatusEvent(models.Model):
""" A status event change on a given `ipsum` for a `lorem`. """
created = models.DateTimeField(auto_now_add=True)
lorem = models.ForeignKey(Lorem)
ipsum = models.CharField(max_length=200)
status = models.CharField(max_length=10)
为了在Django LoremAdmin
(Lorem
模型)中创建自定义QuerySet,我需要:
foo_status
和bar_status
各自从单独的JOIN条款派生到LoremStatusEvent
模型:lorem.loremstatusevent_set__status
。Ipsum
值的事件:foo_status_events=LoremStatusEvent.filter(ipsum='foo')
。foo_status=LoremStatusEvent.objects.filter(ipsum='foo').latest('created').status
。foo_status
和bar_status
注释结果:queryset = queryset.annotate(foo_status=???).annotate(bar_status=???)
。如果我发明了一些功能来完成所有这些 - get_field
,latest_by
,filter_by
,loremstatus_set_of_every_corresponding_lorem
- 我可以写这样的管理员:
from django.contrib import admin
class LoremAdmin(admin.ModelAdmin):
""" Django admin for `Lorem` model. """
class Meta:
model = Lorem
def get_queryset(request):
""" Get the `QuerySet` of all instances available to this admin. """
queryset = super().get_queryset(request)
queryset.annotate(
foo_status=(
get_field('status')(
latest_by('created')(
filter_by(ipsum='foo')(
loremstatusevent_set_of_every_corresponding_lorem)))),
bar_status=(
get_field('status')(
latest_by('created')(
filter_by(ipsum='bar')(
loremstatusevent_set_of_every_corresponding_lorem)))),
)
return queryset
哪些实际功能应该替换每个占位符名称?
loremstatus_set_of_every_corresponding_lorem
,相当于Lorem.loremstatusevent_set
。filter_by
,过滤右侧的联接。latest_by
,用于聚合按字段排序的结果集,以获取单个最新实例。我没有看到任何此类documented aggregation function。get_field
,用于引用结果实例中的字段。请记住,所有这些都需要在Lorem
个实例的查询集上完成,通过Lorem.loremstatusevent_set
访问相关实例;我当时没有LoremStatusEvent
个实例,所以我无法直接使用LoremStatusEvent
的属性。
那么,上面的占位符应该包含哪些实际的Django功能呢?
答案 0 :(得分:4)
您应该可以使用新的Subquery功能执行此操作,该功能也可以backport to 1.8+的形式提供。
try:
from django.db.models.expressions import Subquery, OuterRef
except ImportError:
from django_subquery.expressions import Subquery, OuterRef
class LoremAdmin(admin.ModelAdmin):
# …
def get_queryset(request):
queryset = super().get_queryset(request)
status_event_per_lorem = LoremStatusEvent.objects.filter(
lorem=OuterRef('pk'))
latest_status_event_per_lorem = (
status_event_per_lorem.order_by(
'pk', '-created').distinct('pk'))
latest_status_event_for_ipsum_foo = (
latest_status_event_per_lorem.filter(ipsum='foo'))
latest_status_event_for_ipsum_bar = (
latest_status_event_per_lorem.filter(ipsum='bar'))
queryset = queryset.annotate(
foo_status=Subquery(
latest_status_event_for_ipsum_foo.values('status')[:1]),
bar_status=Subquery(
latest_status_event_for_ipsum_bar.values('status')[:1]),
)
return queryset