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)
Так что я фильтрую дважды и не уверен, почему это должно происходить.