You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何在Django应用中实现单用户仅触发一次指定函数

Django实现同一用户无法重复触发耗时函数

问题分析

你当前的代码通过session标记来阻止重复触发,但存在几个关键问题:

  1. 同步阻塞的sleep会导致请求长时间挂起,用户页面卡顿,且session修改可能因未及时保存,导致并发请求绕过检查
  2. GET请求易被重复提交或浏览器缓存,不符合"无副作用"的HTTP规范
  3. 前端无按钮禁用逻辑,用户可多次点击触发请求

解决方案

1. 后端逻辑优化(核心)

改用POST请求处理操作,结合session或缓存实现原子锁,确保同一用户无法重复触发:

修改后的views.py

from django.shortcuts import render
from django.contrib.auth.decorators import login_required
import time
from django.http import JsonResponse
from django.core.cache import cache

@login_required(login_url="login")
def page1(request):
    if request.method == "POST":
        # 用用户ID生成唯一锁键,高并发场景推荐用缓存原子锁
        lock_key = f"nap_lock_{request.user.id}"
        # 原子设置锁,超时时间设为sleep总时长+1秒,避免异常导致死锁
        if cache.add(lock_key, "locked", timeout=6):
            try:
                sleep(1, 5)
            finally:
                # 无论执行成功与否,都释放锁
                cache.delete(lock_key)
            return JsonResponse({"status": "success", "msg": "操作完成"})
        else:
            return JsonResponse({"status": "error", "msg": "你正在执行操作,请勿重复触发"}, status=400)
    return render(request, 'page1.html')

def sleep(t, n):
    for i in range(n):
        time.sleep(t)
        print(f"nap n°{i}")

修改后的urls.py

from . import views
from django.contrib.auth.decorators import login_required
urlpatterns = [
    path('page1', login_required(views.page1, login_url="login"), name="page1"),
]

2. 前端交互优化

点击按钮后立即禁用,防止用户重复点击,请求完成后恢复按钮状态:

修改后的page1.html

<button type="button" id="nap-btn">NAP</button>

<script>
const napBtn = document.getElementById('nap-btn');

napBtn.addEventListener('click', async () => {
    // 点击后禁用按钮并修改文本
    napBtn.disabled = true;
    napBtn.textContent = '执行中...';
    
    try {
        const response = await fetch("{% url 'page1' %}", {
            method: 'POST',
            headers: {
                'X-CSRFToken': '{{ csrf_token }}',
                'Content-Type': 'application/x-www-form-urlencoded',
            },
        });
        
        const result = await response.json();
        alert(result.msg);
    } catch (error) {
        alert('请求出错,请稍后重试');
    } finally {
        // 恢复按钮状态
        napBtn.disabled = false;
        napBtn.textContent = 'NAP';
    }
});
</script>

方案说明

  • 缓存原子锁:使用cache.add()实现原子操作,比session更适合高并发场景,避免多个请求同时通过检查
  • POST请求:符合HTTP规范,避免GET请求的缓存、重复提交问题
  • 前端禁用:从交互层面直接阻止重复点击,提升用户体验
  • 异常处理:通过try-finally确保锁一定会被释放,避免死锁

内容的提问来源于stack exchange,提问作者problème0123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 17:20:42