29

In order to have a non-readonly PrimaryKeyRelatedField, you are required to provide a queryset that contains valid options.

How can I properly populate that queryset based on the current request (user)?

synic
  • 26,359
  • 20
  • 111
  • 149

2 Answers2

85

The key is to subclass PrimaryKeyRelatedField and overload the get_queryset method, using the user information from the request context:

class UserFilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        request = self.context.get('request', None)
        queryset = super(UserFilteredPrimaryKeyRelatedField, self).get_queryset()
        if not request or not queryset:
            return None
        return queryset.filter(user=request.user)

You can then use this new serializer just like the (unfiltered) original:

class MySerializer(serializers.ModelSerializer):
    related = UserFilteredPrimaryKeyRelatedField(queryset=MyModel.objects)

Whenever the serializer accesses the queryset, it will be filtered such that only objects owned by the current user are returned.

Myk Willis
  • 12,306
  • 4
  • 45
  • 62
  • 1
    This should be marked as answer. I would only add another condition to the if: `or not request.user.is_authenticated`, so it returns None instead of an exception when no user is logged in – angardi Dec 18 '17 at 01:35
  • I'm unable to get the context in the serializer, despite overriding get_serializer_context() in the view. Any pointers on that will be really helpful. – Dhruv Aug 12 '18 at 16:34
  • Update: was able to get the context right, by calling get_serializer() instead of get_serializer_class(), but now the queryset on the serializer is not filtered. It just takes the default(all) queryset that is used while declaring the field on the serailizer. Put a debugger inside of the get_queryset() in the serializer but the code does not hit that part, and I do not understand why. Any pointers will be helpful are appreciated. – Dhruv Aug 12 '18 at 17:04
  • For those struggling, here is a simple example: https://medium.com/django-rest-framework/limit-related-data-choices-with-django-rest-framework-c54e96f5815e – Agey Jul 24 '19 at 13:38
  • `not queryset` will actually perform the query in full and check whether there are any items in it, which may be very slow! Use `queryset is None` instead to check whether there IS a queryset! – Spooner Aug 01 '19 at 00:23
-13

View has a

self.request.user

attribute which you can then use to fetch user related queryset eg

queryset = Products.objects.get(customer=self.request.user)
  • 1
    How do you access the view in the class level of the serializer? – synic Jan 16 '15 at 16:46
  • You can't. You could override the __init__ of the Serializer class to get the view from self.context or even modify the view to pass the logged-in user to the serializer context and use that. The second approach is better because you can test the Serializer individually without requiring to create a view. – Ion Scerbatiuc Mar 20 '15 at 14:33
  • @Fatima request object is not directly exposed in Serializer, work around is context. – Umair Mohammad Nov 23 '18 at 06:51