如何在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
相关产品推荐
相关产品推荐

