How can I, in a single expression, get the latest value from the filtered results of a many-to-one relationship to annotate a Django QuerySet?
Given this toy schema:
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)
For the purpose of making a custom QuerySet in a Django LoremAdmin (for the Lorem model), I need to:
- Derive
foo_statusandbar_statuseach from separate JOIN clauses to aLoremStatusEventmodel:lorem.loremstatusevent_set__status. - Filter each join to only include those events which are for the corresponding
Ipsumvalue:foo_status_events=LoremStatusEvent.filter(ipsum='foo'). - Aggregate the set of status events to only the latest for each corresponding join:
foo_status=LoremStatusEvent.objects.filter(ipsum='foo').latest('created').status. - Annotate the results with a couple of extra values,
foo_statusandbar_status:queryset = queryset.annotate(foo_status=???).annotate(bar_status=???).
If I invent some functions to do all this – get_field, latest_by, filter_by, loremstatus_set_of_every_corresponding_lorem – I could write the admin something like this:
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
What actual functions should replace each of these placeholder names?
loremstatus_set_of_every_corresponding_lorem, the equivalent ofLorem.loremstatusevent_set.filter_by, to filter a join at the right side.latest_by, to aggregate the result set, ordered by a field, to get the single latest instance. I don't see any such documented aggregation function.get_field, to reference a field from the resulting instance.
Remember that all this needs to be done on a queryset of Lorem instances, accessing the related instances via Lorem.loremstatusevent_set; I don't have a LoremStatusEvent instance at that point, so I can't directly use attributes of LoremStatusEvent.
So, what actual Django functionality should be in the above placeholders?