3

Here is my view:

class SectorListAPI(generics.ListAPIView):
    queryset = SectorModel.objects.all()
    serializer_class = SectorSerializer

Here is my serializers:

class OrganizationSerializer(serializers.ModelSerializer):
class Meta:
    model = GroupProfile
    fields = ('title','slug',)



class DepartmentSerializer(serializers.ModelSerializer):
    organizations = OrganizationSerializer(many=True, read_only=True)
    class Meta:
        model = DepartmentModel
        fields = ('title', 'organizations',)


class SectorSerializer(serializers.ModelSerializer):
    # title = serializers.CharField()
    departments = DepartmentSerializer(many=True, read_only=True)

    class Meta:
        model = SectorModel
        fields = ('title','departments',)

Look, here 'SectorSerializer' is parent 'DepartmentSerializer' is children and 'OrganizationSerializer' is grand children serializer. Now in my view I can easily filter my queryset for 'SectorModel'. But how can i filter on 'GroupProfile' model.

3
  • what is group profile? Commented Oct 10, 2018 at 6:32
  • Read docs: django-rest-framework.org/api-guide/filtering Commented Oct 10, 2018 at 6:48
  • 1
    Look, here 'SectorSerializer' is parent 'DepartmentSerializer' is children and 'OrganizationSerializer' is grand children serializer. Now in my view I can easily filter my queryset for 'SectorModel'. But my problem is, I want to filter on 'GroupProfile' model. Commented Oct 10, 2018 at 7:08

2 Answers 2

4

You might want to filter the queryset to ensure that only results relevant to the currently authenticated user making the request are returned.

You can do so by filtering based on the value of request.user.

For example:

from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        """
        This view should return a list of all the purchases
        for the currently authenticated user.
        """
        user = self.request.user
        return Purchase.objects.filter(purchaser=user)

EDIT

You can subclass the ListSerializer and overwrite the to_representation method.

By default the to_representation method calls data.all() on the nested queryset. So you effectively need to make data = data.filter(**your_filters) before the method is called. Then you need to add your subclassed ListSerializer as the list_serializer_class on the meta of the nested serializer.

1- subclass ListSerializer, overwriting to_representation and then calling super

2- Add subclassed ListSerializer as the meta list_serializer_class on the nested Serializer.

Code relevant to yours:

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 OrganizationSerializer(serializers.ModelSerializer):
class Meta:
    list_serializer_class = FilteredListSerializer
    model = GroupProfile
    fields = ('title','slug',)



class DepartmentSerializer(serializers.ModelSerializer):
    organizations = OrganizationSerializer(many=True, read_only=True)
    class Meta:
        model = DepartmentModel
        fields = ('title', 'organizations',)


class SectorSerializer(serializers.ModelSerializer):
    # title = serializers.CharField()
    departments = DepartmentSerializer(many=True, read_only=True)

    class Meta:
    model = SectorModel
    fields = ('title','departments',)
Sign up to request clarification or add additional context in comments.

5 Comments

this will filter my main queryset, not nested or children queryset. Here Sector is parent and Department is children and Organization is grand children. what you said will only filter parent, but i wanna filter grand children.
oh thanks a lot @ans2human and this will be great if it work. Thanks.
@cjahangir Sure, try it out. If it works then hit the check box for this answer.
This is the right answer. But django-rest-framework has changed, so there is no self.request in the serializer anymore, it's in self.context.request, so do something like: request = self.context.get('request') first.
Hey @BjornW, so yes things have changed since this answer was posted. Why don't you edit it and provide the correct syntax. Thanks for pointing it out.
0

Thanks to @ans2human for the inspiration behind this answer.

Here's a new approach that is working great for me. I have several Models with is_active = BooleanField(...) that I need to filter out in nested relationships.

NOTE: this solution does not filter out results on non-list fields. for that, you should look to the primary queryset on your View

The core of the work is done by overloading the to_representation() function on a custom ListSerializer, and the many_init on an accompanying custom ModelSerializer:

class FilteredListSerializer(serializers.ListSerializer):
    filter_params:dict
    def __init__(self, *args, filter_params:dict={"is_active":True}, **kwargs):
        super().__init__(*args, **kwargs)
        self.filter_params = filter_params

    def set_filter(self, **kwargs):
        self.filter_params = kwargs

    def to_representation(self, data):
        data = data.filter(**self.filter_params)
        return super().to_representation(data)

class FilteredModelSerializer(serializers.ModelSerializer):

    LIST_SERIALIZER_KWARGS = serializers.LIST_SERIALIZER_KWARGS + ("filter_params",)
    LIST_ONLY_KWARGS = ('allow_empty', 'filter_params')

    @classmethod
    def many_init(cls, *args, **kwargs):
        list_kwargs = dict()
        for arg in cls.LIST_ONLY_KWARGS:
            value = kwargs.pop(arg, None)
            if value is not None:
                list_kwargs[arg] = value
        child_serializer = cls(*args, **kwargs, **({"read_only":True} if "read_only" not in kwargs else dict()))
        list_kwargs['child'] = child_serializer
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in cls.LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', FilteredListSerializer)
        return list_serializer_class(*args, **list_kwargs)

Then, your custom ModelSerializer for whatever view would instead just extend FilteredModelSerializer instead.

class ChildSerializer(FilteredModelSerializer):
    is_active = BooleanField() # not strictly necessary, just for visibilty
    ... # the rest of your serializer

class ParentSerializer(serializers.ModelSerializer):
    children = ChildSerializer(many=True)
    ...# the rest of your parent serializer

Now, the children field on the ParentSerializer will filter for is_active = True.

If you have a custom query that you wanted to apply, you can do so by providing a dict of filter params in the standard queryset format:

class ParentSerializer(serializers.ModelSerializer):
    children = ChildSerializer(many=True, filter_params={"my_field":my_value, "my_datetime__gte": timezone.now()})
    ...# the rest of your parent serializer

Alternatively, one could also utilize the set_filter(...) method on the FilteredListSerializer after instantiating the field, like so. This will yield a more familiar format to the original QuerySet.filter(...) style:

class ParentSerializer(serializers.ModelSerializer):
    children = ChildSerializer(many=True)
    children.set_filter(my_field=my_value, my_datetime__gte=timezone.now())
    ...# the rest of your parent serializer

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.