Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to response different data that was inserted by create method (ModelViewSet)

I'm implementing a stock shoes manager with REST architecture using Django + Django rest.

Im using a custom Router inherited from DefaultRouter to serve my endpoints.

In the /resources/id endpoint Ive added one more verb, POST that is called by custom_create method.

Here you can see this custom_create method:

viewsets.py


class ShoeViewSet(viewsets.ModelViewSet):

    queryset = Shoe.objects.all()
    filter_class = ShoeFilter

    def get_serializer_class(self):
        if self.action == 'custom_create':
            return StockPostSerializer
        else:
            return ShoeSerializer

    def custom_create(self, request, *args, **kwargs):
        data = {}

        data['shoe'] = kwargs['pk']
        data['size'] = request.data.get('size')
        data['amount'] = request.data.get('amount')

        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

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

I needed to do this because I have two models, below you can see my 3 Serializers:

serializers.py

class StockSerializer(serializers.ModelSerializer):

    class Meta:
        model = Stock
        fields = ['size', 'amount']

class ShoeSerializer(serializers.ModelSerializer):
    stock = StockSerializer(many=True, read_only=True)

    class Meta:
        model = Shoe
        fields = ['description', 'provider', 'type', 'cost_price','sale_price','total_amount', 'stock']

class StockPostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Stock
        fields = ['shoe','size', 'amount']

The retrieve (GET verb) method of this endpoint expects data serialized by ShoeSerializer, but the custom_create method insert data using the StockPostSerializer. How can I return a response with a different data that was inserted ?

When I try to insert with this endpoint I recieve this error message, but when I refresh the page I realize that the content was inserted (If i use postman instead of de DRF frontend I dont get any error message, works fine).

How can my custom_create method Responses correctly ?

You can check my github, the names will be a bit different because I translated it here so that it is easier for you to understand.

PS: As you may have noticed I am not a native speaker of the English language, so it is very difficult to express myself here but I am trying my best, and learning more and more. If my question contains grammar / concordance errors please correct them but you do not have to refuse me so I'm trying to learn!

like image 459
Tâmer Cuba Avatar asked May 26 '19 22:05

Tâmer Cuba


People also ask

What is a ViewSet?

A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as . get() or . post() , and instead provides actions such as . list() and . create() .

What is Perform_create?

perform_create is called within the create method to call the serializer for creation once it's known the serialization is valid. Specifically, serializer.save() Code from the source - when in doubt check it: class CreateModelMixin(object): """ Create a model instance. """

What is Perform_create in Django?

perform_create. perform create method is used to add extra information when creating a new object. perform_create() method will not execute if you override create() method.

What is Django REST framework geeksforgeeks?

Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds. There are three stages before creating a API through REST framework, Converting a Model's data to JSON/XML format (Serialization), Rendering this data to the view, Creating a URL for mapping to the viewset.


2 Answers

I finally managed to sort this out, and in a much more elegant way than I had been trying beforehand.

What I need to do is: add new stock instances, for this I had created a new route for POST in the endpoint resources/id.

So I was able to reuse the Default Router, delete the custom_create method, and just modified the serializers.py file. It looks like this:

serializers.py

class StockSerializer(serializers.ModelSerializer):

    class Meta:
        model = Stock
        fields = ['size', 'amount']

class ShoeSerializer(serializers.ModelSerializer):

    stock = StockSerializer(many=True)

    def update(self, instance, validated_data):
        instance.description   = validated_data.get(
            'description', instance.description)
        instance.provider  = validated_data.get(
            'provider', instance.provider)
        instance.type        = validated_data.get('type', instance.type)
        instance.cost_price = validated_data.get(
            'cost_price', instance.cost_price)
        instance.salve_price = validated_data.get(
            'sale_price', instance.sale_price)

        stock      = instance.stock.all()
        stock_data = validated_data.get('stock', [])

        for item_data in stock_data:
            item_id = item_data.get('size', None)
            if item_id is not None:
                item_db            = stock.get(size=item_id)
                item_db.size    = item_data.get('size', item_db.size)
                item_db.amount = item_data.get('amount',item_db.amount)
                item_db.save()
            else:
                Estoque.objects.create(
                    shoe = instance,
                    size    = item_data['size'],
                    amount = item_data['amount']
                )
        instance.save()
        return instance

    class Meta:
        model = Shoe
        fields = ['_id','description', 'provider', 'type', 'cost_price','sale_price','total_amount', 'stock']

Now, via PATCH verb I can add new Stock instances and alter existing stock instances. Thank you for the support!

like image 170
Tâmer Cuba Avatar answered Oct 09 '22 08:10

Tâmer Cuba


If I understood correctly by looking at your code, in this case specifically, you don't need the StockPostSerializer. You can acheive the result you want by changing StockSerializer as follows:

class StockSerializer(serializers.ModelSerializer):

    class Meta:
        model = Stock
        fields = ['shoe', 'size', 'amount']
        extra_kwargs = {'shoe': {'write_only': True}}

I greatly apologize if I misunderstood your question.

EDIT:

Forgot to say. Using this serializer you don't need any extra route on your ModelViewSet

like image 36
Giacomo Casoni Avatar answered Oct 09 '22 08:10

Giacomo Casoni