为何Django允许两个不同密码提交且无报错?
这种情况我碰到过好几次,大概率是表单校验逻辑缺失或者视图没有触发校验流程导致的。咱们一步步来排查:
1. 先检查你的forms.py:密码确认的校验逻辑有没有加?
Django的ModelForm默认不会帮你做密码确认的校验,你得自己加字段和校验逻辑。如果你的表单是继承自UserCreationForm,它自带password1和password2的校验,但如果你是自定义的表单,或者没正确继承,就会出问题。
错误示例(缺少校验):
from django.forms import ModelForm from .models import CustomUser class RegisterForm(ModelForm): confirm_password = forms.CharField(widget=forms.PasswordInput()) class Meta: model = CustomUser fields = ['username', 'email', 'password']
这个表单只加了confirm_password字段,但没有对比两个密码的逻辑,所以即使不一致也能提交。
正确做法:重写clean()方法或者用内置的校验
方法一:重写clean()做密码对比
from django import forms from django.forms import ModelForm from .models import CustomUser class RegisterForm(ModelForm): confirm_password = forms.CharField(widget=forms.PasswordInput(), label="Confirm Password") class Meta: model = CustomUser fields = ['username', 'email', 'password'] widgets = { 'password': forms.PasswordInput(), } def clean(self): cleaned_data = super().clean() password = cleaned_data.get("password") confirm_pwd = cleaned_data.get("confirm_password") # 校验两个密码是否一致 if password and confirm_pwd and password != confirm_pwd: raise forms.ValidationError("Passwords don't match!") # 还要确保密码符合Django的复杂度要求(可选但推荐) if password: from django.contrib.auth.password_validation import validate_password try: validate_password(password) except forms.ValidationError as e: self.add_error('password', e) return cleaned_data
方法二:直接继承UserCreationForm(更省心)
如果你的自定义用户模型符合Django的用户规范,直接继承UserCreationForm它会自动帮你处理password1和password2的校验:
from django.contrib.auth.forms import UserCreationForm from .models import CustomUser class CustomUserRegisterForm(UserCreationForm): class Meta: model = CustomUser fields = ['username', 'email', 'password1', 'password2']
这个表单自带密码一致性校验和复杂度校验,省得自己写。
2. 检查你的views.py:有没有触发表单校验?
就算表单写了校验逻辑,如果视图里没调用form.is_valid(),那校验根本不会执行!
错误示例(跳过校验):
def register(request): if request.method == 'POST': form = RegisterForm(request.POST) # 直接保存,没做校验 user = form.save(commit=False) user.set_password(form.cleaned_data['password']) user.save() return redirect('login') else: form = RegisterForm() return render(request, 'register.html', {'form': form})
正确做法:必须先调用form.is_valid()
def register(request): if request.method == 'POST': form = RegisterForm(request.POST) if form.is_valid(): # 这一步会触发所有表单校验 user = form.save(commit=False) # 注意:如果用了ModelForm,要确保密码是加密存储的 user.set_password(form.cleaned_data['password']) user.save() return redirect('login') else: form = RegisterForm() return render(request, 'register.html', {'form': form})
form.is_valid()会自动调用你在表单里写的clean()方法,校验不通过的话会把错误信息放到form.errors里,模板里就能显示出来。
3. 额外检查:模板里的表单是否正确渲染?
确保模板里的confirm_password字段(或者password2)的name属性和表单里的字段名一致,不然表单接收不到这个值,校验也等于白写。比如:
<form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Register</button> </form>
如果是手动写的输入框,要保证name="confirm_password"(和表单字段名一致):
<input type="password" name="confirm_password" required>
4. 最后检查managers.py:有没有越俎代庖?
一般来说,自定义用户管理器的create_user方法只负责创建用户、加密密码,不负责校验密码确认。比如正确的管理器应该是这样的:
from django.contrib.auth.base_user import BaseUserManager class CustomUserManager(BaseUserManager): def create_user(self, email, username, password=None, **extra_fields): if not email: raise ValueError('The Email must be set') email = self.normalize_email(email) user = self.model(email=email, username=username, **extra_fields) user.set_password(password) # 这里只负责加密,不校验确认密码 user.save(using=self._db) return user
如果你的管理器里没有做多余的操作,那问题肯定不在这。
按照上面的步骤排查,基本就能解决密码不校验的问题啦!
内容的提问来源于stack exchange,提问作者Rajat Jog

