[Django]-How to ensure idempotency in a Django REST API?

3πŸ‘

I know it is too late to answer this question, but I would like to answer it for those others who might be interested.

By default, GET and DELETE are idempotent since the only possible response states are either 404 or sending data, but not PUT or if we needed to make any POST request idempotent. for PUT the most common way is the validation of input data. Make sure that all the fields of the model(including id) are passed in unless the client receives 400 bad request this way since even id is passed in no one can add unwanted records to the database. another way to be 100% sure everything is idempotent, is we may use cache to store the request body and user’s id as hash into cache server for enough amount of time. something like this:

# in views.py
# for DRF
import hashlib

from django.core.cache import cache
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class IdempotentView(APIView):
     def put(self, req, pk, format=None):

         key = hashlib.blake2b(
                   f"{req.user.username}, {req.body},{pk}".encode("utf-8")
               ).hexdigest()
         is_cached = cache.get(key)

         if is_cached :
            return Response (req.body, status=status.HTTP_201_CREATED)
         
         # preform the view commands then:
         
         expiary  = 60*60*3 # 3 hours 
         cache.set(key, 'True' , exp_date)

         return Response (data, status=status.HTTP_201_CREATED)
πŸ‘€OZahed

0πŸ‘

I believe the following post can shed a light on the topic: Create or Update (with PUT) in Django Rest Framework

Quote:

class ResourceViewSet(viewsets.ModelViewSet):
    """
    This endpoint provides `create`, `retrieve`, `update` and `destroy` actions.
    """
    queryset = Resource.objects.all()
    serializer_class = ResourceSerializer

    def get_object(self):
        if self.request.method == 'PUT':
            resource = Resource.objects.filter(id=self.kwargs.get('pk')).first()
            if resource:
                return resource
            else:
                return Resource(id=self.kwargs.get('pk'))
        else:
            return super(ResourceViewSet, self).get_object()

but more elegant solution which also checks for permissions – is https://gist.github.com/tomchristie/a2ace4577eff2c603b1b

πŸ‘€Serge Vin

Leave a comment