Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework update data before serialize

I want that any user could create their own products with my RESTful API. For this purpose, I created a view that inherits ListCreateAPIView. The problem is that a user should only create products that he/she own, so when the instance of the Product model is created, I wanted that the field Owner corresponds to the user that is authenticated.

Here's my Product model

class Product(models.Model):
    owner = models.ForeignKey(User)
    name = models.CharField(max_length=30)

Obviously my serializer is:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Product

and my view:

class ProductView(generics.ListCreateAPIView):
    queryset = models.Product.objects.all()
    serializer_class = serializers.ProductSerializer
    permission_classes = (IsAuthenticatedOrReadOnly,)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        #serializer.initial_data['owners'] = models.Person.objects.get(user__email=request.user).user_id
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

As you can see, I tried modifying the "initial_data", but it is a QueryDict, which is inmutable. Another option is transforming it into a Python dict and then adding the "owner" value, but at doing that I get every value as a list (i.e. {'name': ['MyName']}), so it would be a very ugly way to solve the problem, I think it should be a more straightforward solution.

like image 538
Andres Espinosa Avatar asked Jul 03 '16 05:07

Andres Espinosa


2 Answers

You can override perform_create() and pass the current user as the owner. Then, when the Product instance will be created, owner field will be saved with the current user value.

class ProductView(generics.ListCreateAPIView):
    queryset = models.Product.objects.all()
    serializer_class = serializers.ProductSerializer
    permission_classes = (IsAuthenticatedOrReadOnly,)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user) # pass the current user as the 'owner'

Also, you should define owner field in your serializer as read_only. Then the field will not be required during deserialization. But, this field will be included in the output on serialization.

 class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Product
        extra_kwargs = {'owner': {'read_only':True}} # set 'owner' as read-only field 
like image 136
Rahul Gupta Avatar answered Sep 29 '22 21:09

Rahul Gupta


Simple solution below. owner will not be visible in request GET/POST/PUT etc. but will be auto assigned to current authenticated user.

from rest_framework.serializers import (CurrentUserDefault, HiddenField,
                                        ModelSerializer)

class ProductSerializer(ModelSerializer):
    owner = HiddenField(default=CurrentUserDefault())

    class Meta:
        model = models.Product
like image 43
dikamilo Avatar answered Sep 29 '22 20:09

dikamilo