Фильтрация вложенных ресурсов Django REST Framework

В начале хочу сказать, что нашел частичный ответ на свой вопрос (Как я могу применить фильтр к вложенному ресурсу в среде Django REST?), но это не решает мою проблему на 100%.

Я вложил 3 модели:

- Модель А

-- МодельAB

---- МодельB

Мне нужно получить всю фильтрацию ModelA по ModelB, но мне также нужно, чтобы ModelB была отфильтрована.

Бывший. /модельA?modelab__modelb__id=100

Ему нужны возвращаемые значения:

[{id:1,
    modelab: [{
        id:10
        modelb{
          id: 100
        }]
    }]
}]

но он возвращается

[{id:1,
    modelab: [{
        id:10
        modelb{
          id: 100
        },
        id:11
        modelb{
          id: 102
        }]
    }]
}]

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

Мой код:

// models.py
class ModelA(models.Model):
    name = models.TextField()
class ModelB(models.Model):
    name = models.TextField()
class ModelAB(models.Model):
    modelA = models.ForeignKey(ModelA)
    modelB = models.ForeignKey(ModelB)
    type_id = models.IntegerField(default=0)

// views.py
class ModelAFilter(filters.FilterSet):

    vacancy_id = Filter(name="vacancy__id")
    class Meta:
        model = Candidate
        fields = ('name', 'modelAB__modelB__name')

class ModelAViewSet(viewsets.ModelViewSet):
    queryset = ModelA.objects.all()
    serializer_class = ModelASerializer

    filter_backends = (filters.DjangoFilterBackend, )
    filter_class = ModelFilter

// serializers.py
class ModelBSerializer(serializers.ModelSerializer):

    modelB = ModelB(many=True)

    class Meta:
        model = ModelB
        fields = ('id', 'name')

class ModelABSerializer(serializers.ModelSerializer):

    modelB= ModelBSerializer(many=True)

    class Meta:
        model = ModelB
        fields = ('id', 'name', 'modelB',)

class ModelASerializer(serializers.ModelSerializer):

    modelAB = ModelAB(many=True)

    class Meta:
        model = ModelA
        fields = ('id', 'name', 'modelAB')

person macher    schedule 19.05.2017    source источник
comment
добавьте код, который вы используете в вопросе   -  person zaphod100.10    schedule 19.05.2017
comment
Добавлен код и изменено название   -  person macher    schedule 19.05.2017


Ответы (1)


Вы должны переопределить метод get_queryset в своем наборе представлений и отфильтровать запрос prefetch_related:

def get_queryset(self):
    # get the filter value
    modelb_id = self.request.query_params.get('modelab__modelb__id', None)
    queryset_modelab = ModelAB.objects.all()
    # you can use django-filter class here too
    if modelb_id:
        queryset_modelab = queryset_modelab.filter(modelb_id=modelb_id)
    queryset_modelab.select_related('modelb')
    queryset = (ModelA.objects
                .prefetch_related(
                    Prefetch('modelab_set', queryset=queryset_modelab))
                .all())
    # modela filter class will work on this queryset
    return queryset
person zaphod100.10    schedule 19.05.2017
comment
Я думал о том, но что, когда у него не будет фильтра или когда будет больше полей для фильтрации? Мне нужно проверить, существуют ли они... Я думал, есть ли какой-нибудь динамический способ сделать это. В SQLAlchemy я мог бы сделать это с помощью await_loading - person macher; 19.05.2017
comment
да, обязательно надо будет проверить, присутствует фильтр или нет. Я немного отредактирую ответ для большей ясности. - person zaphod100.10; 19.05.2017