Reputation: 2974
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
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
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
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