Snig501
Snig501

Reputation: 174

DRF Filter PrimaryKeyField Based on Current User

I have a view set up to return a list of books to a user, which is retrieved from a simple book model based on the currently logged-in user. However, I also have ReadingSession model which has a foreign key relationship to both the Book, and the User.

When I'm retrieving the books for the user, I'd like to, at the very least, return a list of primary keys that I can use to get the length of in my client.

The following code will get the full set of readingsessions in my BookSerializer:

from rest_framework import serializers
from books.models import Book


class BookSerializer(serializers.ModelSerializer):
    readingsession_set = serializers.PrimaryKeyRelatedField(
        many=True, read_only=True)

    class Meta:
        model = Book
        fields = ["id", "title", "author", "publisher",
                  "publish_date", "description", "category",
                  "language", "small_thumbnail", "thumbnail",
                  "readingsession_set"]

However, the problem with this is that it will return all of the readingsessions, regardless of whether or not the session belongs to that user.

I'd like to be able to filter that so that it will only return the readingsessions for the current user. Something along the lines of:

readingsession_set = serializers.PrimaryKeyRelatedField(queryset=ReadingSession.objects.filter(user=user), read_only=True)

But I've tried various ways of trying to pass the user (self.request.user) from the APIView but none seem to work. I've tried passing a context, and tried passing extra **kwargs in __init__ but none seem to work.

Is there a way of achieving this? Or am I taking the wrong approach?

Thanks

Upvotes: 6

Views: 1686

Answers (3)

Alexander Prus
Alexander Prus

Reputation: 31

Found right approach in another similar question: How i can to filter queryset by current user in django rest framework Works perfectly for me:

class PrimaryKeyRelatedFieldByUser(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        query_set = super().get_queryset()
        request = self.context.get('request')
        
        return query_set.filter(user=request.user)

Just don't forget to set base queryset like:

foo = PrimaryKeyRelatedFieldByUser(queryset=Foo.objects.all())

Upvotes: 2

user4850501
user4850501

Reputation:

The user is not present on the serializer's declaration but during its instantiation. Therefore, you can filter querysets by user within the __init__ method.

from rest_framework import serializers

from bar.models import Foo


class RandomSerializer(serializers.Serializer):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        user_foos = Foo.objects.filter(user=self._user)
        self.fields['foo_ids'] = serializers.PrimaryKeyRelatedField(
            required=False,
            many=True,
            read_only=False,
            queryset=user_foos,
            default=user_foos)

    @property
    def _user(self):
        request = self.context.get('request', None)
        if request:
            return request.user

Don't forget to pass the request object to the serializer in the context (if necessary, e.g., using a simple APIView.

from rest_framework import views


class RandomView(views.APIView):
    serializer_class = RandomSerializer

    def post(self, request):
        serializer = self.serializer_class(
            data=request.data, context={'request': request}) 
        # ...

serializer = RandomSerializer(data=request.data, context={'request': request}

Upvotes: 6

ohduran
ohduran

Reputation: 811

You can access the user of the request on the serializer by means of the context.

As mentioned in the documentation, you can always do:

serializer = AccountSerializer(account, context={'request': request})

Thus, you will be able to use self.context['request'].user inside your serializer.

Hope that's what you're after.

Upvotes: 1

Related Questions