Administro un casillero físico con Django (DRF). Los usuarios completan un formulario, se autentican a través de un enlace enviado a su correo electrónico, autorizan a través de un pin que se muestra en el casillero.
Mi vista debe manejar tres casos:
Si el usuario se autentica y autoriza con éxito, el PIN que se muestra en el casillero se reemplaza con un mensaje genérico y se abre el casillero. (Ya implementado)
Si el usuario no puede autorizar dentro de los 3 minutos, el pin del casillero se reemplaza con un mensaje genérico.
Si el usuario Foo realiza una nueva solicitud de autorización, mientras que la autorización para el usuario Bar aún está incompleta, coloque la solicitud en una cola y espere a que se complete el caso 1. o el caso 2.
Cómo puedo:
Ver tal cual, en caso de que sea útil:
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})en su modelo en el que guarda la autorización, necesita agregar un campo de fecha_creada o fecha_solicitada y debe ser un campo de fecha y hora. luego, en cada solicitud, puede verificar si han pasado 3 minutos desde la fecha solicitada que guardó. también necesita un campo is_authorized para verificar si su usuario está autorizado o no. asumimos que está obteniendo el usuario del correo electrónico que envió.
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")Debería almacenar cuándo comenzó la última autorización, borrar esa marca de tiempo si el usuario ingresa el 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) El código anterior muestra cómo lo haría usando la base de datos como la cola, en un caso de uso de bajo volumen, esto debería estar bien. Si el volumen es alto, almacenaría el casillero last_authorization en un almacenamiento más rápido, como redis , la cola también se puede mantener en redis . Pero la lógica detrás sería la misma.