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

如何在Django模板中使用While Loop实现登录次数限制?

首先得指出你给的示例代码里有几个小问题哦:变量名混用了cnt和i,还有cnt =+1应该写成cnt +=1,不然没法正确实现计数逻辑~

回到你的需求:限制用户登录尝试次数,达到上限后禁止登录,在Django里我们不建议直接在模板里写while循环这类逻辑——因为Django的模板语言(DTL)是为展示层设计的,复杂的业务逻辑应该放在视图或模型中处理,模板只负责根据后端传递的数据做渲染展示。

下面给你两种常见场景的实现方案:

1. 会话级限制(仅当前浏览器会话内限制尝试次数)

这种方式下,用户关闭浏览器后尝试次数会重置,适合临时限制登录尝试。

后端视图逻辑(views.py)

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login

def user_login(request):
    # 从session获取已失败次数,默认0
    failed_attempts = request.session.get('failed_attempts', 0)
    max_allowed_attempts = 5  # 设置最大尝试次数

    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)

        if user:
            # 登录成功,重置失败次数
            request.session['failed_attempts'] = 0
            login(request, user)
            return redirect('your_home_page')  # 跳转到登录后的页面
        else:
            # 登录失败,次数+1并保存到session
            failed_attempts += 1
            request.session['failed_attempts'] = failed_attempts

    # 判断是否达到限制次数
    is_login_blocked = failed_attempts >= max_allowed_attempts

    # 把需要的数据传递给模板
    return render(request, 'login.html', {
        'failed_attempts': failed_attempts,
        'max_attempts': max_allowed_attempts,
        'is_blocked': is_login_blocked
    })

前端模板(login.html)

模板里不用写while循环,只需要根据后端传递的状态做条件渲染:

{% extends 'base.html' %}

{% block content %}
<h2>用户登录</h2>

{% if is_blocked %}
    <div class="alert alert-danger">
        您的登录尝试次数已达上限,请稍后再试!
    </div>
{% else %}
    {% if failed_attempts > 0 %}
        <div class="alert alert-warning">
            登录失败,还有 {{ max_attempts - failed_attempts }} 次尝试机会
        </div>
    {% endif %}

    <form method="POST">
        {% csrf_token %}
        <div class="form-group">
            <label>用户名:</label>
            <input type="text" name="username" class="form-control" required>
        </div>
        <div class="form-group">
            <label>密码:</label>
            <input type="password" name="password" class="form-control" required>
        </div>
        <button type="submit" class="btn btn-primary">登录</button>
    </form>
{% endif %}
{% endblock %}

2. 永久级限制(数据库记录总失败次数,永久禁止)

如果需要永久限制用户登录(比如累计失败5次就锁定账号),需要给用户模型添加一个字段来记录失败次数:

扩展User模型(models.py)

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    failed_login_attempts = models.IntegerField(default=0)
    is_login_locked = models.BooleanField(default=False)

视图逻辑调整(views.py)

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from .models import CustomUser

def user_login(request):
    max_allowed_attempts = 5

    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        
        try:
            user = CustomUser.objects.get(username=username)
        except CustomUser.DoesNotExist:
            user = None

        if user and user.is_login_locked:
            # 账号已锁定,直接返回提示
            return render(request, 'login.html', {'is_blocked': True})

        if user and authenticate(request, username=username, password=password):
            # 登录成功,重置失败次数和锁定状态
            user.failed_login_attempts = 0
            user.is_login_locked = False
            user.save()
            login(request, user)
            return redirect('your_home_page')
        elif user:
            # 登录失败,更新失败次数
            user.failed_login_attempts += 1
            if user.failed_login_attempts >= max_allowed_attempts:
                user.is_login_locked = True
            user.save()

    # 处理GET请求,获取用户状态
    username = request.GET.get('username')
    is_blocked = False
    remaining_attempts = max_allowed_attempts
    if username:
        try:
            user = CustomUser.objects.get(username=username)
            is_blocked = user.is_login_locked
            if not is_blocked:
                remaining_attempts = max_allowed_attempts - user.failed_login_attempts
        except CustomUser.DoesNotExist:
            pass

    return render(request, 'login.html', {
        'is_blocked': is_blocked,
        'remaining_attempts': remaining_attempts
    })

模板调整(login.html)

{% extends 'base.html' %}

{% block content %}
<h2>用户登录</h2>

{% if is_blocked %}
    <div class="alert alert-danger">
        您的账号已因多次登录失败被锁定,请联系管理员解锁!
    </div>
{% else %}
    {% if remaining_attempts < 5 %}
        <div class="alert alert-warning">
            登录失败,还有 {{ remaining_attempts }} 次尝试机会
        </div>
    {% endif %}

    <form method="POST">
        {% csrf_token %}
        <div class="form-group">
            <label>用户名:</label>
            <input type="text" name="username" class="form-control" required>
        </div>
        <div class="form-group">
            <label>密码:</label>
            <input type="password" name="password" class="form-control" required>
        </div>
        <button type="submit" class="btn btn-primary">登录</button>
    </form>
{% endif %}
{% endblock %}

总结一下:Django模板的核心是展示,业务逻辑(比如计数、判断限制)一定要放在后端视图或模型里,这样代码更清晰、更安全,也符合Django的MVT设计理念。

内容的提问来源于stack exchange,提问作者Chidananda Nayak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:44:44