2015-01-27 2 views
34

В Django Rest Framework, как вы фильтруете сериализатор, когда он вложен в другой сериализатор?Как фильтровать вложенный сериализатор в Django Rest Framework?

Мои фильтры накладываются на виды просмотра DRF, но когда вы вызываете сериализатор из другого сериализатора, просмотр вложенного сериализатора никогда не вызывается, поэтому вложенные результаты нефильтруются.

Я попытался добавить фильтр на исходном представлении, но он, кажется, не фильтрует вложенные результаты, потому что вложенные результаты вызывают как отдельный предварительно отлаженный запрос. (Вы видите, что вложенный сериализатор - обратный поиск.)

Можно ли добавить переопределение get_queryset() в самом вложенном сериализаторе (перемещение его из вида), чтобы добавить туда фильтр? Я тоже пробовал это, не повезло.

Это то, что я пытался, но она даже не кажется, дозвонились:

class QuestionnaireSerializer(serializers.ModelSerializer): 
    edition = EditionSerializer(read_only=True) 
    company = serializers.StringRelatedField(read_only=True) 

    class Meta: 
     model = Questionnaire 

    def get_queryset(self): 
     query = super(QuestionnaireSerializer, self).get_queryset(instance) 
     if not self.request.user.is_staff: 
      query = query.filter(user=self.request.user, edition__hide=False) 
     return query 

Любая помощь оценили

Джон

+3

'get_queryset' - это класс на ModelViewSet, а не на Serializer, поэтому его не назовут – Simon

ответ

42

Вы можете подкласс ListSerializer и перезаписать метод to_representation ,

По умолчанию метод to_representation вызывает data.all() на вложенном наборе запросов. Поэтому перед вызовом метода вам необходимо сделать data = data.filter(**your_filters). Затем вам нужно добавить свой подклассифицированный ListSerializer как list_serializer_class в мета-вложенном сериализаторе.

  1. подкласс ListSerializer, перезапись to_representation и затем вызвать супер
  2. добавить подклассы ListSerializer как мета list_serializer_class на вложенной Serializer

Вот соответствующий код для образца.

class FilteredListSerializer(serializers.ListSerializer): 

    def to_representation(self, data): 
     data = data.filter(user=self.request.user, edition__hide=False) 
     return super(FilteredListSerializer, self).to_representation(data) 


class EditionSerializer(serializers.ModelSerializer): 

    class Meta: 
     list_serializer_class = FilteredListSerializer 
     model = Edition 


class QuestionnaireSerializer(serializers.ModelSerializer): 
    edition = EditionSerializer(read_only=True) 
    company = serializers.StringRelatedField(read_only=True) 

    class Meta: 
     model = Questionnaire 
+1

Это было трюк! Хотя, в конце концов, я решил, что мои сериализаторы становятся слишком сложными, и я реорганизовал их все, заставляя клиента запускать еще несколько вызовов api, но значительно упростив мое приложение. – John

+1

Попытка использовать это как основу для решения аналогичной проблемы; не уверен, действительно ли он заслуживает собственного вопроса. Как передать var из 'QuestionnaireSerializer' в ListSerializer? Чтобы приблизиться, мне нужно отфильтровать идентификатор версии, а также идентификатор Анкеты. – Brendan

+0

Внутри представления, отберите объект от отношения ребенка к родительскому. Все, что вы делаете, это нечто вроде данных [0] .question, позволяющих вам вернуться назад. Кажется немного взломанным, но он должен работать. Имейте в виду, что данные уже должны быть отфильтрованы на основе его отношения к родительскому идентификатору. – inperspective

2

Протестировано множество решений от SO и других мест.

Найдено только одно рабочее решение для Django 2.0 + DRF 3.7.7.

Определить метод в модели с вложенным классом. Создайте фильтр, который будет соответствовать вашим потребностям.

class Channel(models.Model): 
    name = models.CharField(max_length=40) 
    number = models.IntegerField(unique=True) 
    active = models.BooleanField(default=True) 

    def current_epg(self): 
     return Epg.objects.filter(channel=self, end__gt=datetime.now()).order_by("end")[:6] 


class Epg(models.Model): 
    start = models.DateTimeField() 
    end = models.DateTimeField(db_index=True) 
    title = models.CharField(max_length=300) 
    description = models.CharField(max_length=800) 
    channel = models.ForeignKey(Channel, related_name='onair', on_delete=models.CASCADE) 

.

class EpgSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Epg 
     fields = ('channel', 'start', 'end', 'title', 'description',) 


class ChannelSerializer(serializers.ModelSerializer): 
    onair = EpgSerializer(many=True, read_only=True, source="current_epg") 

    class Meta: 
     model = Channel 
     fields = ('number', 'name', 'onair',) 

Обратите внимание на source="current_epg" и вы получите точку.

+0

Это работает и в Django 1.9 – feydaykyn

Смежные вопросы