Andres Espinosa
Andres Espinosa

Reputation: 402

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.

Upvotes: 10

Views: 8587

Answers (3)

Artem Bernatskyi
Artem Bernatskyi

Reputation: 4667

You can pass initial data in view and then rewrite default value in serializer

views.py

class ItemListCreateView(ListCreateAPIView):

    def get_serializer_context(self):
        return {"delivery_request": self.kwargs['req_pk']}

serializers.py

class ItemCreateSerializer(ModelSerializer):
    delivery_request = CharField(default='')

    def validate_delivery_request(self, value):
        try:
            value = DeliveryRequest.objects.get(
                id=self.context["delivery_request"])
        except DeliveryRequest.DoesNotExist:
            raise ValidationError(
                self.Meta.error_messages['delivery_request_not_found'])
        return value

PS. it is useful when you want to populate not Null data from url for example

Upvotes: 1

dikamilo
dikamilo

Reputation: 667

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

Upvotes: 1

Rahul Gupta
Rahul Gupta

Reputation: 47856

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 

Upvotes: 11

Related Questions