如何在Django中对请求进行排队?

2024-06-01 14:20:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我与Django(DRF)一起管理一个物理储物柜。用户填写表单,通过发送到其电子邮件的链接进行身份验证,通过储物柜上显示的pin进行授权

我的观点应该处理三种情况:

  • 如果用户成功进行身份验证和授权,则在 锁柜将替换为一般消息,锁柜将打开。 (已实施)

  • 如果用户未能在3分钟内授权,则locker pin将替换为通用消息

  • 如果用户Foo发出了新的授权请求,而用户Bar的授权仍然不完整,则将该请求堆叠在 排队等待案例1。或案例2。完成

我该怎么办:

  • 实现一个请求队列,以便在收到新请求时不会覆盖/替换储物柜上显示的pin
  • 在处理下一个请求之前,如何等待3分钟以完成授权

按原样查看,如果有用:

   if request.method == 'POST':
        form = ConfirmationForm(request.POST)
        if form.is_valid():
            if pin == form.cleaned_data['pin']:
                open_bay(jwt_token=jwt[1], pin=pin)
                display_generic_message(jwt_token=jwt[1])
                lock_bay(jwt_token=jwt[1], pin=pin)
                return render(request, 'static/pages/request-success.html')
            else:
                pass
    else:
        form = ConfirmationForm()
    return render(request, 'static/pages/confirmation.html', {'form': form})

Tags: 用户formtoken身份验证消息ifrequestpin
2条回答

在您保存授权的模型中,您需要添加创建日期或请求日期的字段,并且必须是日期时间字段。然后在每个请求上,您可以检查从您保存的请求日期起是否已过3分钟。 您还需要一个is_authorized字段来检查您的用户是否被授权。 我们假设您从发送的电子邮件中获取用户

user = get_object_or_404(User,email=kwargs['email'])
if user.date_request + timedelta(minutes=3) > datetime.datetime.now():
   "do your authorzing stuff ..."
else:
     return HttpResponse("you need to wait 3 minutes to request again")

您需要存储上次授权启动的时间,如果用户输入pin,则清除该时间戳

# models.py
class LockerUserQueue(models.Model):

    user = models.ForeignKey(get_user_model())
    locker = models.ForeignKey("yourapp.Locker")
    created_at = models.DateTimeField(index=True, auto_now_add=True)


class Locker(models.Model):
    last_authorization = models.DateTimeField(null=True, blank=True)
    user_queue = models.ManyToManyField(through=LockerUserQueue)

    class BadPin(Exception):
        pass

    def enqueue_user(self, user):
        self.user_queue.add(user)

    def process_authorization(self, user):
        # do authorization for user

    def process_pin(self, user, pin):
        self.last_authorization = None
        if validate_pin(user, pin):
            # pin OK logic
        else:
            raise self.BadPin

# views.py
def authorize_user(request, locker_id):
    locker = get_object_or_404(pk=locker_id)
    locker.enqueue_user(request.user)
    locker.save()
    return render(request, "authorization_started.html")

def open_bay_with_pin(request, locker_id):
    locker = get_object_or_404(pk=locker_id)
    pin = get_pin_from_request(request)
    try:
        # No matter if the pin is correct, the locker.last_authorization is cleared
        locker.process_pin(user, pin)
    except locker.BadPin:
        return render(request, "bad_pin.html")
    finally:
        locker.save()
    return render(request, "good_pin.html")



# management/commands/process_queue.py
# you would run this by
# $ python manage.py process_queue
class Command(BaseCommand):

    @transaction.atomic
    def process_queue(self):
        # you probably want to put the 3 min delay in your settings.py
        # so you don't end up with a magic value here
        for locker in LockerUserQueue.objects.filter(
            Q(locker__last_authorization__isnull=True)|Q(locker__last_authorization__gt=now() - timedelta(minutes=3))
        ).order_by("created_at").values_list("locker", flat=True).distinct():
            locker.process_authorization()

    def handle(self):
        while True:
            self.process_queue()
            # you don't want to keep querying the DB when there's nothing in the queue
            sleep(5)

上面的代码显示了如何使用数据库作为队列,在低容量用例中,这应该很好。如果卷很大,我会将储物柜last_authorization存储在更快的存储中,比如redis,队列也可以在redis中维护。但背后的逻辑是一样的

相关问题 更多 >