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

Django 6+Bootstrap 5实现带确认密码验证的用户注册表单

Django + Bootstrap 注册表单:添加确认密码字段及字段级验证优化

问题分析

现有代码存在几个核心问题:

  • password2 是额外字段,不应放在 ModelForm 的 Meta.widgets 和 Meta.error_messages 中,这些配置仅针对模型字段
  • 未实现密码一致性验证逻辑
  • 模板中手动渲染 password2 输入框,无法利用Django表单的自动错误绑定机制
  • change_class_for_error 函数冗余,可通过表单或模板逻辑简化

优化后的完整实现

1. models.py(无需修改,建议生产环境改用密码哈希存储)

class UserMaster(models.Model):
    username = models.CharField(max_length=50, unique=True)
    password = models.CharField(max_length=15)
    
    def __str__(self):
        return self.username

注意:生产环境禁止明文存储密码,推荐使用django.contrib.auth.models.AbstractUser或make_password函数哈希密码

2. forms.py:添加额外字段+密码验证逻辑

from django import forms
from .models import UserMaster

class UserMasterForm(forms.ModelForm):
    # 单独定义确认密码字段(非模型字段)
    password2 = forms.CharField(
        label="Confirm Password",
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Confirm Password'}),
        required=True,
        error_messages={'required': 'Confirmation password cannot be empty'}
    )

    class Meta:
        model = UserMaster
        fields = ['username', 'password']
        widgets = {
            'username': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter User Name'}),
            'password': forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Enter Password'}),
        }
        error_messages = {
            'username': {
                'required': 'Username cannot be empty',
                'unique': 'Username already taken'
            },
            'password': {
                'required': 'Password cannot be empty'
            }
        }

    # 重写clean方法,验证密码一致性
    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        password2 = cleaned_data.get('password2')

        if password and password2 and password != password2:
            # 将错误绑定到password2字段,确保错误信息显示在对应位置
            self.add_error('password2', 'Passwords do not match')
        
        return cleaned_data

3. views.py:简化逻辑,移除冗余函数

from django.shortcuts import render, redirect
from .forms import UserMasterForm

def register_view(request):
    if request.method == 'POST':
        form = UserMasterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
        # 无需手动修改字段class,模板可通过表单字段的errors状态自动判断
    else:
        form = UserMasterForm()
    
    return render(request, 'mainapp/register.html', {'form': form})

4. signup.html:结合Django表单渲染与Bootstrap样式

<form action="" class="g-3 mx-2" method="POST" novalidate>
    {% csrf_token %}
    <div class="row mb-3">
        <div class="col-md-5">
            <label class="form-label">{{ form.username.label }}</label>
            {{ form.username }}
            {% if form.username.errors %}
                {% for error in form.username.errors %}
                    <div class="invalid-feedback d-block">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-md-6">
            <label class="form-label">{{ form.password.label }}</label>
            {{ form.password }}
            {% if form.password.errors %}
                {% for error in form.password.errors %}
                    <div class="invalid-feedback d-block">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>
        <div class="col-md-6">
            <label class="form-label">{{ form.password2.label }}</label>
            {{ form.password2 }}
            {% if form.password2.errors %}
                {% for error in form.password2.errors %}
                    <div class="invalid-feedback d-block">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>
    </div>
    <div class="flex gap-3 mt-3 mb-2">
        <button class="btn btn-primary button-label rounded-0 w-25" type="submit">
            Save
        </button>
        <button class="btn btn-warning button-label rounded-0 w-25" type="button">
            Cancel
        </button>
    </div>
</form>

<style>
    /* 自动给有错误的字段添加Bootstrap的is-invalid样式 */
    {% for field in form %}
        {% if field.errors %}
            #id_{{ field.name }} {
                border-color: #dc3545;
            }
        {% endif %}
    {% endfor %}
</style>

核心优化点说明

  • 额外字段规范:直接在表单类中定义password2,而非混入模型字段的Meta配置,符合Django表单设计规范
  • 验证逻辑集中:通过重写clean()方法实现密码一致性检查,并用add_error()将错误精准绑定到password2字段
  • 样式自动处理:移除手动修改字段class的冗余函数,通过模板判断或CSS自动给错误字段添加Bootstrap样式
  • 模板统一渲染:使用表单字段的原生属性生成标签和输入框,避免手动编写HTML导致的错误绑定失效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 20:24:50