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

如何在Django中过滤关联对象,仅返回匹配的关联数据?

Django关联对象过滤问题解决

问题场景

请求localhost:8000/api/status?status_details__action=INCORRECT时,返回的StatusGroup对象会包含该组下所有StatusDetail条目,但实际需求是仅返回action为INCORRECT的关联StatusDetail对象。

现有代码

模型代码

class StatusGroup(models.Model):
    name = models.TextField()

    def __str__(self):
        return self.name


class StatusDetail(models.Model):
    action = models.CharField(choices=[("CORRECT", "CORRECT"),
                                       ("INCORRECT", "INCORRECT")],
                              max_length=64)
    status_group = models.ForeignKey(to=StatusGroup,
                                      on_delete=models.CASCADE,
                                      related_name="status_details")

    def __str__(self):
        return f"Detail: {self.action}"

序列化器代码

class StatusDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model= models.StatusDetail
        fields = "__all__"

class StatusGroupSerializer(serializers.ModelSerializer):
    status_details = StatusDetailSerializer(many=True)

    class Meta:
        model = models.StatusGroup
        fields = [
            "pk",
            "status_details",
            "name"
        ]

视图代码

class Status(viewsets.ModelViewSet):
    queryset = models.StatusGroup.objects.all()
    serializer_class = serializers.StatusGroupSerializer
    authentication_classes = []
    permission_classes = [permissions.AllowAny]

    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['status_details__action']

解决方法

方法一:序列化阶段动态过滤关联数据

重写StatusGroupSerializer的to_representation方法,根据请求中的过滤参数筛选关联的status_details:

from rest_framework import serializers
import models

class StatusDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model= models.StatusDetail
        fields = "__all__"

class StatusGroupSerializer(serializers.ModelSerializer):
    status_details = StatusDetailSerializer(many=True)

    class Meta:
        model = models.StatusGroup
        fields = [
            "pk",
            "status_details",
            "name"
        ]

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # 获取请求中的action过滤参数
        action_filter = self.context['request'].query_params.get('status_details__action')
        if action_filter:
            # 过滤status_details
            filtered_details = [
                detail for detail in representation['status_details']
                if detail['action'] == action_filter
            ]
            representation['status_details'] = filtered_details
        return representation

方法二:数据库查询阶段预过滤(性能更优)

使用Django的Prefetch对象,在查询StatusGroup时就预取过滤后的StatusDetail,减少序列化阶段的内存操作:

from django.db.models import Prefetch
from rest_framework import viewsets, permissions
from django_filters.rest_framework import DjangoFilterBackend
import models
import serializers

class Status(viewsets.ModelViewSet):
    serializer_class = serializers.StatusGroupSerializer
    authentication_classes = []
    permission_classes = [permissions.AllowAny]

    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['status_details__action']

    def get_queryset(self):
        queryset = models.StatusGroup.objects.all()
        # 获取过滤参数
        action = self.request.query_params.get('status_details__action')
        if action:
            # 构造预取查询,过滤指定action的StatusDetail
            prefetch = Prefetch(
                'status_details',
                queryset=models.StatusDetail.objects.filter(action=action)
            )
            queryset = queryset.prefetch_related(prefetch)
        return queryset

方法对比

  • 方法一:在序列化阶段对已查询到的所有关联数据进行过滤,实现简单,但如果关联数据量较大,会占用更多内存。
  • 方法二:在数据库查询阶段就完成过滤,仅获取符合条件的关联数据,性能更优,适合数据量较大的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 15:50:38