Django rest framework: исключение результатов, когда вложенный сериализатор пуст

У меня есть вложенный сериализатор, который работает, но я хотел бы исключить случаи, когда вложенный сериализатор пуст. Фильтрация, которую я использую во вложенном сериализаторе, работает, но в настоящее время этот код возвращает все Сайты, большинство из которых имеют пустые массивы site_observations при применении фильтров. Я бы хотел возвращать только те Сайты, которые содержат site_observations. Я пробовал использовать SerializerMethodField для site_observations, но возникла та же проблема. Использую DRF 3.12

Релевантными моделями являются Site, и Observation, которая имеет FK к Site, с 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 объектами, для которых существуют site_observation отношения. Здесь необходим вызов distinct, описанный this.

Спасибо за предложение, которое привело к ответу. Отказ от запуска 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

UPDATE AUG 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)

Так что я фильтрую дважды и не уверен, почему это должно происходить.

Вернуться на верх