簡體   English   中英

在 Django 中使用最新的相關對象進行注釋

[英]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_relatedqueryset 就像是:

Conversation.objects.prefetch_related(
    Prefetch('messages'),
    queryset=Message.objects.order_by('date').first()
)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM