You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django/DRF查询Order列表时关联最新OrderStatus的实现问题

问题描述

我有一个返回Order对象列表的API端点,模型定义如下:

Order模型:

class Order(models.Model):
    item_name = models.CharField(max_length=120)

每个Order对应多条OrderStatus记录(类似包裹追踪,状态变更时创建带时间戳的新记录),OrderStatus模型:

class OrderStatus(models.Model):
    status = models.CharField(max_length=30)
    timestamp = models.DateTimeField(auto_now_add=True)
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order_statuses")

现在需要查询Order列表时,返回每个Order关联的最新OrderStatus,期望返回格式:

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "item_name": "Something",
            "order_status": {
                "status": "Processing",
                "timestamp": "2023-09-25T12:31:12Z"
            }
        },
        {
            "item_name": "Other Thing",
            "order_status": {
                "status": "Completed",
                "timestamp": "2023-08-15T10:41:32Z"
            }
        }
    ]
}

试过用prefetch_related(),但它适合反向查询OrderStatus关联Order的场景,满足不了当前需求,求可行实现方法。


可行实现方法

方法1:annotate+子查询(推荐,无N+1查询)

通过Django的子查询功能,直接在Order查询阶段关联最新的OrderStatus记录,避免额外的数据库请求。

视图代码:

from django.db.models import OuterRef, Subquery
from rest_framework import generics
from .models import Order, OrderStatus
from .serializers import OrderSerializer

class OrderListView(generics.ListAPIView):
    serializer_class = OrderSerializer

    def get_queryset(self):
        # 构造子查询:筛选当前Order的最新状态记录
        latest_status_subquery = OrderStatus.objects.filter(
            order=OuterRef('pk')
        ).order_by('-timestamp')[:1]

        # 给每个Order附加最新状态的字段
        return Order.objects.annotate(
            latest_status=Subquery(latest_status_subquery.values('status')),
            latest_timestamp=Subquery(latest_status_subquery.values('timestamp'))
        )

序列化器代码:

from rest_framework import serializers
from .models import Order

class OrderSerializer(serializers.ModelSerializer):
    order_status = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ['item_name', 'order_status']

    def get_order_status(self, obj):
        if hasattr(obj, 'latest_status') and hasattr(obj, 'latest_timestamp'):
            return {
                'status': obj.latest_status,
                'timestamp': obj.latest_timestamp
            }
        return None

方法2:自定义Prefetch对象

如果偏好使用prefetch_related,可以自定义Prefetch规则,只预取每个Order的最新状态记录,而非全部状态。

视图代码:

from django.db.models import Prefetch
from rest_framework import generics
from .models import Order, OrderStatus
from .serializers import OrderSerializer

class OrderListView(generics.ListAPIView):
    serializer_class = OrderSerializer

    def get_queryset(self):
        # 定义预取规则:只获取每个Order的最新1条状态
        latest_status_prefetch = Prefetch(
            'order_statuses',
            queryset=OrderStatus.objects.order_by('-timestamp')[:1],
            to_attr='latest_order_status'
        )

        return Order.objects.prefetch_related(latest_status_prefetch)

序列化器代码:

from rest_framework import serializers
from .models import Order

class OrderSerializer(serializers.ModelSerializer):
    order_status = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ['item_name', 'order_status']

    def get_order_status(self, obj):
        if obj.latest_order_status:
            status = obj.latest_order_status[0]
            return {
                'status': status.status,
                'timestamp': status.timestamp
            }
        return None

方法3:序列化器直接查询(仅小数据量场景)

如果数据量很小,可以直接在序列化器中查询最新状态,但这种方法会触发N+1查询(每个Order单独查一次OrderStatus),不推荐大数据量使用。

序列化器代码:

from rest_framework import serializers
from .models import Order

class OrderSerializer(serializers.ModelSerializer):
    order_status = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ['item_name', 'order_status']

    def get_order_status(self, obj):
        latest_status = obj.order_statuses.order_by('-timestamp').first()
        if latest_status:
            return {
                'status': latest_status.status,
                'timestamp': latest_status.timestamp
            }
        return None

内容的提问来源于stack exchange,提问作者Brian C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 23:11:30