[英]Annotate with latest related object in Django
我有一個模型Conversation
和一個模型Message
。
模型Message
具有對話的外鍵、文本字段和日期字段。
如何列出所有對話並為每個對話獲取最新消息和最新消息的日期?
我想這有點像
Conversation.objects.annotate(last_message=Max('messages__date'))
但它只會給我最新的日期。 我希望last_message
包含最后一條消息的文本和它的創建日期。 也許我需要使用 prefetch_related ?
從 Django 1.11 開始,您可以使用子查詢表達式:
latest_message = Subquery(Message.objects.filter(
conversation_id=OuterRef("id"),
).order_by("-date").values('value')[:1])
conversations = Conversation.objects.annotate(
latest_message=latest_message,
)
使用子查詢表達式注釋最新值的助手:
from django.db.models import OuterRef, Subquery
from django.db.models.query_utils import DeferredAttribute
def latest_value(attr: DeferredAttribute, ref: DeferredAttribute, outref: str = 'id', order_by=None, **kwargs):
"""Annotation helper to annotate last value of related table to queryset
attr - attr as Class.Field which need to be attached, model will be taken from it
ref - as Class.Field by which field will be used for join with main table
outref - as field_name (str) of main table to join with ref
order_by - optional order field, by default will be used attr field
kwargs - optional extra filtering kwargs for join
"""
assert attr.field.model == ref.field.model, "Need to use same models"
db_field = attr.field
field_name = db_field.attname
filters = {ref.field.attname: OuterRef(outref), **kwargs}
order_by = order_by or f"-{field_name}"
sub_query = db_field.model.objects.filter(**filters).order_by(order_by)
return Subquery(sub_query.values(field_name)[:1], output_field=db_field.__class__())
對於指定的示例,它應該看起來像
q_value = latest_value(Message.value, ref=Message.conversation, order_by="-date")
conversations = Conversation.objects.annotate(latest_message=q_value)
您還可以將子查詢與django.db.models.functions.JSONObject
(在 Django 3.2 中添加)結合起來一次獲取多個字段,而無需添加多個子查詢:
Conversation.objects.annotate(
last_object=Message.objects.filter(conversation=OuterRef("pk"))
.order_by("-date_created")
.values(
data=JSONObject(
id="id", body="body", date_created="date_created"
)
)[:1]
)
您可以使用prefetch_related
和queryset
。 就像是:
Conversation.objects.prefetch_related(
Prefetch('messages'),
queryset=Message.objects.order_by('date').first()
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.