Django中UserCreationForm无法检测用户名已存在的问题排查
问题描述
我基于Django的UserCreationForm做了个注册表单,想检测用户名和邮箱是否已存在数据库里,但现在只有邮箱重复能检测出来,用户名重复没反应。以下是我的代码:
Views.py
from django.shortcuts import render,redirect from . forms import signupform from django.contrib import messages from django.contrib.auth import login,authenticate,logout from django.contrib.auth.models import User def signup_data(request): form = signupform(request.POST) if form.is_valid(): username = form.cleaned_data['username'] email = form.cleaned_data['email'] if User.objects.filter(username=username).exists(): messages.error(request,'Username is already taken') return redirect('signup') elif User.objects.filter(email=email).exists(): messages.error(request,'Email is already taken') return redirect('signup') else: form.save() messages.success(request,'Account Is Created') return redirect('signup') return render(request,'login_module/signup.html',{'form':form, 'message': messages})
Forms.py
from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User class signupform(UserCreationForm): username= forms.CharField(max_length=10,widget=forms.TextInput(attrs={'class':'form-control'})) first_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'class': 'form-control'})) last_name = forms.CharField(max_length=20,widget=forms.TextInput(attrs={'class': 'form-control'})) email = forms.EmailField(max_length=20,widget=forms.EmailInput(attrs={'class': 'form-control'})) password1 = forms.CharField(label="Password",widget=forms.PasswordInput(attrs={'class':'form-control'})) password2 = forms.CharField(label="Confirm Password",widget=forms.PasswordInput(attrs={'class':'form-control'})) class Meta: model = User fields = ['username','first_name','last_name','email','password1','password2']
问题原因
你的视图逻辑根本没机会执行到用户名重复的判断——UserCreationForm本身会自动验证用户名的唯一性(Django内置User模型的username字段是唯一约束),当用户名重复时,form.is_valid()直接返回False,代码根本进不了后面的判断分支,自然不会触发你写的用户名重复提示。而邮箱的验证UserCreationForm默认没做,所以你的邮箱判断逻辑能正常跑。
另外你在表单里重新定义了username字段,但没保留原有的验证逻辑,同时视图里没处理表单验证失败的情况,错误信息也没传到模板显示。
解决方案
方案一:调整视图逻辑(快速修复)
先区分请求方法,GET请求时初始化空表单;POST请求时,先判断表单是否有效,无效就直接渲染表单(表单自带错误信息),有效再单独检查邮箱:
from django.shortcuts import render,redirect from .forms import signupform from django.contrib import messages from django.contrib.auth.models import User def signup_data(request): if request.method == 'POST': form = signupform(request.POST) if form.is_valid(): email = form.cleaned_data['email'] # 只需要单独检查邮箱,用户名的唯一性表单已经验证过了 if User.objects.filter(email=email).exists(): messages.error(request, 'Email is already taken') return redirect('signup') form.save() messages.success(request, 'Account Is Created') return redirect('signup') else: # GET请求时创建空表单 form = signupform() # 表单验证失败或GET请求时,把表单(含错误信息)传给模板 return render(request, 'login_module/signup.html', {'form': form})
方案二:把验证逻辑移到表单(推荐,符合Django规范)
把邮箱的唯一验证写到表单类里,让表单自己处理所有验证逻辑,视图只负责处理提交,代码更清晰:
修改后的Forms.py
from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.core.exceptions import ValidationError class signupform(UserCreationForm): username = forms.CharField(max_length=10, widget=forms.TextInput(attrs={'class':'form-control'})) first_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'class': 'form-control'})) last_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'class': 'form-control'})) email = forms.EmailField(max_length=20, widget=forms.EmailInput(attrs={'class': 'form-control'})) password1 = forms.CharField(label="Password", widget=forms.PasswordInput(attrs={'class':'form-control'})) password2 = forms.CharField(label="Confirm Password", widget=forms.PasswordInput(attrs={'class':'form-control'})) class Meta: model = User fields = ['username','first_name','last_name','email','password1','password2'] # 自定义邮箱验证 def clean_email(self): email = self.cleaned_data.get('email') if User.objects.filter(email=email).exists(): raise ValidationError("Email is already taken") return email # 可选:自定义用户名重复的错误提示(不写的话用Django默认提示) def clean_username(self): username = self.cleaned_data.get('username') if User.objects.filter(username=username).exists(): raise ValidationError("Username is already taken") return username
简化后的Views.py
from django.shortcuts import render,redirect from .forms import signupform from django.contrib import messages def signup_data(request): if request.method == 'POST': form = signupform(request.POST) if form.is_valid(): form.save() messages.success(request, 'Account Is Created') return redirect('signup') else: form = signupform() return render(request, 'login_module/signup.html', {'form': form})
模板显示错误信息
不管用哪个方案,都需要在模板里添加错误显示逻辑,比如在表单上方加:
{% if form.errors %} {% for field in form %} {% for error in field.errors %} <div class="alert alert-danger">{{ error }}</div> {% endfor %} {% endfor %} {% endif %}
内容的提问来源于stack exchange,提问作者Rahul
相关产品推荐
相关产品推荐

