A. J. Parr
A. J. Parr

Reputation: 8026

How to filter serializers.SlugRelatedField queryset using model field

I am having trouble filtering the possible options for a SlugRelatedField using the queryset parameter. Here is my serializer

class AttendeeProfileSerializer(serializers.HyperlinkedModelSerializer):
    """
    Profile Serializer
    """
    user = serializers.SlugRelatedField(slug_field='username', queryset=User.objects.all())
    module = serializers.SlugRelatedField(slug_field='id', queryset=AttendeeModule.objects.all())
    picture = serializers.ImageField(allow_empty_file=False, required=False)

    class Meta:
        model = AttendeeProfile
        fields = (
            'user', 'module', 'title', 'company', 'email', 'picture', 'is_active', 'created'
        )

I would like to filter the module field though, a la serializers.SlugRelatedField(slug_field='id', queryset=AttendeeModule.objects.filter(module__app='module__app') or something similar where each module belongs to an app.

I imagine I need to provide more context to the serializer through the view? Should I be overriding a method on the view? I've tried but I'm still new to Django rest framework and it's giving me a lot of trouble

The view is

class AttendeesList(generics.ListCreateAPIView):
    """

    """
    queryset = AttendeeProfile.objects.all()
    serializer_class = AttendeeProfileSerializer

And using this route

url(r'^apps/(?P<url_name>[a-z][a-z0-9]+)/modules/(?P<module_id>[0-9]+)/attendees$',
    views.AttendeesList.as_view(),
    name='attendees-list'),

Upvotes: 7

Views: 6002

Answers (3)

Stefan_EOX
Stefan_EOX

Reputation: 1529

I'm using the context parameter of the serializer.

class SlugRelatedModuleField(SlugRelatedField):

    def get_queryset(self):
        return self.queryset.filter(module__app_id=self.context["app_id"])

Context can be set in a view(set) like this:

class AttendeeProfileViewSet(viewsets.ModelViewSet):
    queryset = AttendeeProfile.objects.all()
    serializer_class = AttendeeProfileSerializer

    def get_serializer_context(self) -> Dict[str, Any]:
        return {
            **super().get_serializer_context(),
            "app_id": self.request.data.get("app_id")
        }

Upvotes: 0

atb00ker
atb00ker

Reputation: 1105

In my case, I needed access to request.user as well for filtering, for which I see no solution above, so here is a sample code:

class MyFieldName(serializers.SlugRelatedField):
    def get_queryset(self):
        queryset = MyModel.objects.all()
        request = self.context.get('request', None)
        if not request.user.is_superuser:
            queryset = queryset.filter(user=request.user)
        return queryset

class MySerializer(serializers.ModelSerializer):
    organization = MyFieldName(slug_field='slug')

Upvotes: 4

A. J. Parr
A. J. Parr

Reputation: 8026

So I found a solution to my ancient question:

class SlugRelatedModuleField(SlugRelatedField):

    def get_queryset(self):
        queryset = self.queryset
        if hasattr(self.root, 'app_id'):
            queryset = queryset.filter(module__app_id=self.root.app_id)
        return queryset

class AttendeeProfileSerializer(ModelSerializer):

    def __init__(self, *args, **kwargs):
        self.app_id = kwargs.pop('app_id')
        super().__init__(*args, **kwargs)

    module = SlugRelatedModuleField(
        slug_field='id', 
        queryset=AttendeeModule.objects.all()
    )

    class Meta:
        model = AttendeeProfile
        fields = ('user', 'module', 'title', 'company', 'email', 'picture', 'is_active', 'created')

This assigns an app_id attribute on the parent/root serializer, and the SlugRelatedModuleField inspects the parent to filter the queryset. Tada.

Upvotes: 5

Related Questions