Django rest framework:当嵌套序列化程序为空时排除结果

Django rest framework: exclude results when nested serializer is empty

我有一个可用的嵌套序列化程序,但我想排除嵌套序列化程序为空的实例。我在嵌套序列化程序上使用的过滤有效,但目前此代码 return 是所有站点,其中大多数站点在应用过滤器时具有空 site_observations 数组。我只想 return 包含 site_observations 的站点。我已经为 site_observations 尝试了 SerializerMethodField 但遇到了同样的问题。使用 DRF 3.12

相关模型是 Site 和具有 FK 的 Observation,related field=site_observations

serializers.py

class FilteredObsListSerializer(serializers.ListSerializer):
    def to_representation(self, data):
        projName = self.context["projName"]
    # this is my filter which works
        data = filter_site_observations(data, self.context["request"],
                                        projName)
        return super(FilteredObsListSerializer, self).to_representation(data)


class ObsFilterSerializer(serializers.ModelSerializer):
    class Meta:
        list_serializer_class = FilteredObsListSerializer
        model = Observation
        fields = "__all__"

class SiteSerializer(GeoFeatureModelSerializer):
    site_observations = ObsFilterSerializer(many=True)

    class Meta:
        model = Site
        geo_field = "geometry"
        fields = ("id", "name", "geometry", "site_observations")


views.py

class SiteList(generics.ListAPIView):
    queryset = Site.objects.all().order_by("pk")
    serializer_class = SiteSerializer
    
    # this is for filtering Observations on segment of an url:
    def get_serializer_context(self):
        context = super(SiteList, self).get_serializer_context()
        context.update({"projName": self.kwargs["name"]}) 
        return context

如何排除 site_observations 为空列表的网站?谢谢

一种方法是告诉您的视图仅适用于满足某些条件的特定对象:

class SiteList(generics.ListAPIView):
    queryset = Site.objects.filter(
        site_observations__isnull=False,
    ).distinct().order_by('pk')

这将告诉 SiteList 仅使用存在 site_observation 关系的 Site 对象。您需要按照 this.

所述在此处调用 distinct

感谢您的建议,这导致了一个答案。序列化程序中的 运行ning filter_site_observations 不起作用。我在视图中将过滤器重新设计为 运行 并获得了我需要的结果。 (我在使用 SerializerMethodField 之前尝试过这个,但无法正确过滤它;嵌套序列化程序似乎是更好的方法。)感谢您的建议!这是过滤器:

def filter_site_observations(queryset, request, projName):
    '''
    filters a collection on project, observer status
    '''
    if request.user.is_authenticated:
        authprojects = [
            item.project.name
            for item in ObserverStatus.objects.filter(observer=request.user.id)
        ]
        if projName in authprojects:
            return queryset.filter(site_observations__project__name=projName)
        else:
            return queryset.filter(
                site_observations__project__name=projName).filter(
                    site_observations__private=False)
    else:
        return queryset.filter(
            site_observations__project__name=projName).filter(
                site_observations__private=False)

和 views.py:

class SiteList(generics.ListAPIView):
    serializer_class = SiteSerializer

    def get_serializer_class(self, *args, **kwargs):
        if self.request.method in ("POST", "PUT", "PATCH"):
            return SitePostSerializer
        return self.serializer_class

    def get_queryset(self):
        projName = self.kwargs["name"]
        queryset = Site.objects.all().order_by('pk')
        queryset = filter_site_observations(queryset, self.request, projName)
        queryset = queryset.filter(
            site_observations__isnull=False, ).distinct()
        queryset = self.get_serializer_class().setup_eager_loading(queryset)
        return queryset

8 月 28 日更新 实际上,为了获得我需要的过滤,我必须 运行 在 Observation 序列化程序和 SiteList views.py 中使用几乎相同的过滤器。无论我对子数据使用 SerializerMethodField 还是简单的嵌套序列化程序,都是如此。否则,我会得到:1) 所有网站,包括没有任何观察的网站,或 2) 有一些非私人观察但也显示所有私人观察的网站。

filters.py

from users.models import ObserverStatus


def filter_site_observations(queryset, request, projName):
    '''
    filters a collection on project, observer status; used in views.py.
    Both of these filters seem to be required to get proper filtering.
    '''
    queryset = queryset.filter(site_observations__project__name=projName)

    if request.user.is_authenticated:
        authprojects = [
            item.project.name
            for item in ObserverStatus.objects.filter(observer=request.user.id)
        ]
        if projName in authprojects:
            return queryset
        else:
            return queryset.filter(site_observations__private=False)
    else:
        return queryset.filter(site_observations__private=False)


def filter_observations(queryset, request, projName):
    '''
    filters observations in ObsSerializer on observer status
    '''
    if request.user.is_authenticated:
        authprojects = [
            item.project.name
            for item in ObserverStatus.objects.filter(observer=request.user.id)
        ]
        if projName in authprojects:
            return queryset
        else:
            return queryset.filter(private=False)
    else:
        return queryset.filter(private=False)

所以我过滤了两次,不确定为什么需要这样。