SHIVAM JINDAL
SHIVAM JINDAL

Reputation: 2974

How to handle parallel request in Django Rest Framework?

I have created an API in DRF which accepts POST request with some data, But sometimes I feel that same requests are coming in parallel which causing the duplicate data in DB.

class Feedback(models.Model):
    user = models.ForeignKey(Student)
    message = models.CharField(max_length=255)

Use can send same feedback multiple time. Let's think it's an open API so anyone can consume it and in someone's app user clicks multiple time on button and I received multiple requests but data should be saved only one time.

I have tried by adding a BooleanField to Student table and used the following code to prevent it. But as multiple requests coming in parallel they can read the same value True.

if student.can_submit_feedback:
   student.can_submit_feedback = False
   student.save()
else:   
   # Code for saving feedback
   student.can_submit_feedback = True
   student.save() 

I want to process only one API call on same endpoint and same IP at a time. How can I achieve it?

Updated

I have researched and found that we can add lock on table or object but I am looking for prevention on requests level

Upvotes: 2

Views: 5257

Answers (3)

Raphael Schubert
Raphael Schubert

Reputation: 359

If you still have issues, you can do in this way:

from django.db import transaction
import datetime


@transaction.atomic
def my_view_function(student: Student):

    student = Student.objects.filter(
        id=student.id, 
        last_feedback=student.last_feedback
    ).update(
        last_feedback=datetime.datetime.now()
    )

    if student is True:
        # feedback models and rules
        pass

Then when you store it on database you will do:

try:
    my_view_function(student)
except:
    print("Unable to issue the feedback as other feedback is just issued")

Upvotes: 0

SHIVAM JINDAL
SHIVAM JINDAL

Reputation: 2974

The parallel requests can be prevented by using throttling in DRF. Basic configuration are as follow:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day', # 100 requests per day, 
        'user': '1000/day'
    }
}

day can be replaced by second, minute, hour or day as per the requirement.

If you want to configure different rate limits for different request method like GET, POST and PUT. You can simply create your own throttle. Here is the example of throttle for GET request.

class CustomBaseThrottle(rest_throttles.SimpleRateThrottle):
    """
    Limits the rate of API calls.

    The IP address, url and request method will be used for make unique key for anonymous user.
    The user id, url and request method will be used for make unique key for authenticated user.
    """
    scope = 'get'

    def get_cache_key(self, request, view):

        if request.method.lower() == self.scope:
            if is_authenticated(request.user):
                return "{}-{}-{}".format(request.user.id, request.path, request.method)
            return "{}-{}-{}".format(self.get_ident(request), request.path, request.method)
        return None

Refer http://www.django-rest-framework.org/api-guide/throttling/ for more details

Upvotes: 2

fiveclubs
fiveclubs

Reputation: 2431

It sounds like you want to enforce some uniqueness in your model. You didn't provide any code, but here's a Student model, for example:

class Student
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)
    date_of_birth = models.DateField()
    admission_date = models.DateField()

    class Meta:
        unique_together = ['first_name', 'last_name', 'date_of_birth', 'admission_date']

Then, in your view that creates the student, you'll have to return an HTTP error code if the student already exists:

def create_student(request):
    new_student = Student(first_name=request.POST['first_name'],
                          last_name=request.POST['last_name'],
                          date_of_birth=request.POST['date_of_birth'],
                          admission_date=request.POST['admission_date'])
    try:
        new_student.save()
    except IntegrityError:
        response = HttpResponse("Student already created")
        response.status_code = 409    # code for conflict
        return response

    return HttpResponse("OK, new student created")

NOTE: You might want to think about your design if you will have multiple duplicate students being created in parallel.

Upvotes: 0

Related Questions