cxstam
cxstam

Reputation: 333

Django Rest Framework: Update object in ModelSerializer based on arbitrary data received from ModelViewSet - looking for a better solution

I have a ModelViewSet defined as below:

class PurchaseOrderApproverViewSet(viewsets.ModelViewSet):
    queryset = PurchaseOrderApprover.objects.all()

    @list_route(methods=['POST'])
    def approve_purchase_order(self, request):
        purchase_order = request.data['purchase_order']
        approver = request.data['approver']
        purchase_order_approver = 
        PurchaseOrderApprover.objects.filter(approver=approver, 
        purchase_order=purchase_order).first()
        if purchase_order_approver:
            serializer = PurchaseOrderApproverSerializer()
            serializer.approve(purchase_order_approver)
            content = {'success'}
            return Response(content, status=status.HTTP_200_OK)
        else:
            raise Http404

and the corresponding ModelSerializer is:

class PurchaseOrderApproverSerializer(serializers.ModelSerializer):

    class Meta:
        model = PurchaseOrderApprover
        exclude = ('purchase_order', )

    @transaction.atomic()
    def approve(self, instance):
        instance.is_approved = True
        purchase_order = instance.purchase_order
        all_approved = True

        for purchase_order_approver in PurchaseOrderApprover.objects.filter(purchase_order=purchase_order):
            if not purchase_order_approver.is_approved:
                all_approved = False

        if all_approved:
            purchase_order.status = APPROVED
            purchase_order.save()
        instance.save()
        return instance

Assume request.data will contain a json payload: {"purchase_order": 1, "approver": 1}, based on those two attributes, I can find the corresponding purchase_order_approver, then, I want to update is_approved to True via the method approve defined in PurchaseOrderApproverSerializer.

the model is defined as below:

class PurchaseOrderApprover(BaseModel):
    purchase_order = models.ForeignKey(PurchaseOrder, related_name='purchase_order_approvers', on_delete=models.CASCADE)
    approver = models.ForeignKey(User, on_delete=models.PROTECT)
    is_approved = models.BooleanField(default=False)

Because the serializer can only receive data that have been defined in the serializer, so, I have to find the object via a filter in the viewset first, then, initiate a serializer to call the function defined in the serializer to update the object. But I am pretty sure DRF provides a better (or simpler) way to do it.

I am looking for something such as customized viewset method or override the update method in serializer to achieve this purpose.

Upvotes: 0

Views: 470

Answers (1)

Luan Fonseca
Luan Fonseca

Reputation: 1517

Maybe, inside your serializer method approve(), you can use the Serializer's context, then you have access to the request. Based on that you should get the request.data.

Inside your Serializer method:

request = self.context.get('request')

# In cases of your data came as a GET params 
print(request.query_params)
# Normal post/patch/put method request data
print(request.data)

Upvotes: 1

Related Questions