10

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.

3 Answers 3

11

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 
Sign up to request clarification or add additional context in comments.

2 Comments

The problem with that is that the perform_create function is called after is_valid() execution, and the serializer is not valid without the owner field, so the create function will raise an exception before perform_create is executed.
You can define owner field as read_only in your serializer. Then it will not be required during deserialization but will always be returned in the output upon serialization.
1

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

Comments

1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.