Jon Kennedy
Jon Kennedy

Reputation: 572

Is the Django Rest Framework thread safe?

I have a Django Rest API to return a value.
This API may be hit at the same time/
Is django rest api thread safe?

    $http.get('//0.0.0.0:8000/api/tempName?format=json')
        .success(function (data) {
              $scope.iterativeNum = data.iterativeField
        })

In the views.py

class ApiDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Api.objects.all()
    serializer_class = ApiSerializer

    def get(self, request, *args, **kwargs):
        currentForm = self.kwargs['pk']
        currentApi = Api.objects.get(pk = currentItem)
        currentApi.currentNumber += 1

        tempCurrentNum = currentApi.currentNumber
        strCurrentNum = str(tempCurrentNum)
        currentNumLength = len(strCurrentNum)

        if(currentNumLength > currentApi.maxNumberOfDigitsInNum):
            currentApi.currentNumber = 1

        currentApi.iterativeField = currentApi.fieldPrefix + str(currentApi.currentNumber)
        currentApi.save()
        return self.retrieve(request, *args, **kwargs)

I am concerned two seperate calls can hit the 'ApiDetail' view and return the same number before.

I'm working on a Mac using Python/Django. Django 1.9.4, Django Rest 3.3.3.

Upvotes: 2

Views: 2348

Answers (2)

C14L
C14L

Reputation: 12558

You could wrap everything into a transaction with transaction.atomic()

def get(self, request, *args, **kwargs):
    with transaction.atomic()
        currentApi = Api.objects.get(pk=currentItem)
        ...
        currentApi.save()

    return self.retrieve(request, *args, **kwargs)

Upvotes: 1

Joey Wilhelm
Joey Wilhelm

Reputation: 5819

I believe the problem here will be more your code, than django-rest-framework. What you want to use is an F() expression, as documented here: https://docs.djangoproject.com/en/1.9/ref/models/expressions/#f-expressions

Specifically, you will want to read the section on "Avoiding race conditions using F()".

You will end up with something along the lines of:

from django.db.models import F
from rest_framework import generics


class ApiDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Api.objects.all()
    serializer_class = ApiSerializer

    def get(self, request, *args, **kwargs):
        currentApi = self.get_object()
        currentApi.currentNumber = F('currentNumber') + 1
        currentApi.iterativeField = F('fieldPrefix') + (F('currentNumber') + 1)
        currentApi.save()
        return super(ApiDetail, self).get(request, *args, **kwargs)

As far as the maxNumberofDigits, I will leave that as an exercise for the reader. But, I would personally recommend doing an absolute number max, rather than a max number of digits. You could then easily accomplish the above using Case()/When() and an F(). (For example, max 6 digits, the max number would be 999999).

Upvotes: 0

Related Questions