如何在 Django Rest Framework 中处理并行请求?

Posted

技术标签:

【中文标题】如何在 Django Rest Framework 中处理并行请求?【英文标题】:How to handle parallel request in Django Rest Framework? 【发布时间】:2018-11-05 04:33:38 【问题描述】:

我在 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 调用。我怎样才能实现它?

更新

我研究并发现我们可以在表或对象上添加锁,但我正在寻找请求级别的预防

【问题讨论】:

@dfundako 我进行了很多研究并尝试了多种解决方案,但没有一个适合我,这就是我在这里发布我的问题的原因。 也许你也可以在客户端解决,禁用提交按钮直到你的请求结束。 @aliva 你是对的,但我们也应该在服务器端处理它,因为 API 可以对任何人开放。 【参考方案1】:

可以通过在 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可以根据需要替换为secondminutehourday

如果您想为不同的请求方法(如 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】:

听起来您想在模型中强制执行一些唯一性。您没有提供任何代码,但这里有一个 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")

注意:如果要并行创建多个重复的学生,您可能需要考虑您的设计。

【讨论】:

将所有领域的唯一性放在一起并不是一个好主意。让模型有两个字段 user 和 message 并且用户可以多次发送相同的消息,但是当 API 命中与同一个 IP 并行时,我想防止这种情况发生。 我已经更新了问题,请再看看。【参考方案3】:

如果还是有问题,可以这样操作:

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")

【讨论】:

以上是关于如何在 Django Rest Framework 中处理并行请求?的主要内容,如果未能解决你的问题,请参考以下文章

Django.rest_framework:如何序列化一对多?

如何使用 TemplateHTMLRenderer 在 Django-REST-Framework 中创建/放置?

django-rest-framework:如何序列化已经包含 JSON 的字段?

如何在 Django Rest Framework 中散列 Django 用户密码?

如何仅使用 django 作为后端并使用 django-rest-framework 发布

如何在 React 中显示来自 django-rest-framework 的错误消息