我已经在 DRF 中创建了一个API,它接受带有一些数据的POST请求,但有时我觉得相同的请求是并行的,导致数据库中的重复数据。
class Feedback(models.Model):
user = models.ForeignKey(Student)
message = models.CharField(max_length=255)
使用可以多次发送相同的反馈。让我们认为它是一个开放的API,所以任何人都可以使用它,在某人的应用程序中,用户点击多次按钮,我收到了多个请求,但数据只能保存一次。
我尝试过添加BooleanField
到Student
表并使用以下代码来阻止它。但是,当多个请求并行时,它们可以读取相同的值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()
我想一次只处理同一端点和同一IP上的一个API调用。我怎样才能实现它?
更新
我已经研究过并发现我们可以在表或对象上添加锁定,但我正在寻求对请求级别的预防
答案 0 :(得分:0)
听起来你想在你的模型中强制实现某些独特性。您没有提供任何代码,但这里是Student
模型,例如:
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']
然后,在您创建学生的视图中,如果学生已经存在,您将不得不返回HTTP错误代码:
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")
注意:如果您要同时创建多个重复的学生,您可能需要考虑您的设计。
答案 1 :(得分:0)
可以通过在DRF中使用限制来防止并行请求。基本配置如下:
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
可以根据要求由second
,minute
,hour
或day
替换。
如果要为不同的请求方法(如GET,POST和PUT)配置不同的速率限制。你可以简单地创建自己的油门。以下是GET
请求的限制示例。
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
有关详细信息,请参阅http://www.django-rest-framework.org/api-guide/throttling/
答案 2 :(得分:0)
如果仍然有问题,可以通过以下方式进行:
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
然后,当您在数据库上store
进行操作时,您将这样做:
try:
my_view_function(student)
except:
print("Unable to issue the feedback as other feedback is just issued")