Sapnesh Naik
Sapnesh Naik

Reputation: 11656

DRF Set OneToOneField to currently logged in user

I have a model which has a OneToOneField like this:

class Customer(models.Model):
    """A Class to represent a Customer in  System """

    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)

The serializer for which looks like this:

class CustomerSerializer(serializers.ModelSerializer):
    """A Serizlier class for customer """

    class Meta:
        model = models.Customer
        fields = ('user', 'first_name', 'last_name', 'dob', 'gender') 

Right now the browsable api shows a dropdown list for user

enter image description here

how can I set user to currently logged in user in CustomerSerializer and make it read_only ? i.e the field should not be a passable parameter to the endpoint and instead should be a read only field corresponding to currently logged in user.

Upvotes: 0

Views: 261

Answers (3)

JPG
JPG

Reputation: 88689

First of all, you should set the user to a read_only field. By doing so, you can't alter the value from API. This can be done by specifying read_only=True in the field OR done by adding the read_only_fields in the serializer's Meta class.

class CustomerSerializer(serializers.ModelSerializer):
    """A Serizlier class for customer """

    class Meta:
        model = models.Customer
        fields = ('user', 'first_name', 'last_name', 'dob', 'gender')
        read_only_fields = ('user',)


Thus, DRF won't accept any input to the user field from your JSON payload that you are POSTing.


Then, How can we pass the logged-in user value while Customer instance creation?

I assume that you are using ModelViewset view, so that it will pass the request object and view object to the corresponding serializer. In serializer we can access the context data by context class variable of the serializer.
So, override the create() method of CustomerSerializer as below (I'm adding the complete config of the serializer bellow)

class CustomerSerializer(serializers.ModelSerializer):
    """A Serizlier class for customer """

    class Meta:
        model = models.Customer
        fields = ('user', 'first_name', 'last_name', 'dob', 'gender')
        read_only_fields = ('user',) # added <<<<

    def create(self, validated_data): # override this method <<<
        """
        Since the 'user' is a read_only field, the validated data doesn't contain it.
        """
        user = self.context['request'].user
        validated_data['user'] = user
        return super().create(validated_data)

Upvotes: 2

bkawan
bkawan

Reputation: 1361

You can do it by overriding get_fields(self) of CustomerSerializer. Passing Current request user and filter user quer

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = ['user', 'first_name', 'last_name']

    def get_fields(self):
        fields = super().get_fields()
        current_user = self.context['request'].user
        fields['user'] = serializers.PrimaryKeyRelatedField(queryset=User.objects.filter(id=current_user.id))
        return fields

Upvotes: 0

gonczor
gonczor

Reputation: 4146

I would limit choices in viewset.

class MyViewset(ViewSet):
    serializer_class = CustomerSerializer
    def get_queryset(self):
        return Customer.objects.filter(user=self.request.user)

Upvotes: 0

Related Questions