Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotate with latest related object in Django

I have a model Conversation and a model Message.

The model Message has a foreign key to conversation, a text field, and a date field.

How can I list all conversations and for each conversation get the most recent message and the date of the most recent message?

I guess it's something like

Conversation.objects.annotate(last_message=Max('messages__date'))

but it will only give me the latest date. I want last_message to contain both the text of the last message and the date it was created. Maybe I need to use prefetch_related?

like image 992
Jamgreen Avatar asked May 29 '15 11:05

Jamgreen


People also ask

What is difference between annotate and aggregate Django?

Unlike aggregate() , annotate() is not a terminal clause. The output of the annotate() clause is a QuerySet ; this QuerySet can be modified using any other QuerySet operation, including filter() , order_by() , or even additional calls to annotate() .

How does annotate work in Django?

Django annotations 2 are a way of enriching the objects returned in QuerySets. That is, when you run queries against your models you can ask for new fields, whose values will be dynamically computed, to be added when evaluating the query. These fields will be accessible as if they were normal attributes of a model.

What is QuerySet annotate?

What is an annotation? Annotate generate an independent summary for each object in a queryset. In simpler terms, annotate allows us to add a pseudo field to our queryset. This field can then further be used for filter lookups or ordering.\

Why annotate is used in Django?

The Django ORM is a convenient way to extract data from the database, and the annotate() clause that you can use with QuerySets is also a useful way to dynamically generate additional data for each object when the data is being extracted.


1 Answers

Since Django 1.11, you could use Subqueries expressions:

latest_message = Subquery(Message.objects.filter(
    conversation_id=OuterRef("id"),
).order_by("-date").values('value')[:1])

conversations = Conversation.objects.annotate(
    latest_message=latest_message,
)
like image 80
moppag Avatar answered Sep 17 '22 17:09

moppag