You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DjangoBlog/accounts/forms.py

166 lines
7.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.core.exceptions import ValidationError
from django.forms import widgets
from django.utils.translation import gettext_lazy as _
from . import utils # 导入自定义工具模块(用于验证码验证等功能)
from .models import BlogUser # 导入自定义用户模型
# 模块级注释——accounts应用的前台用户交互表单配置文件
# 包含登录、注册、忘记密码、验证码获取等核心业务表单,
# 负责用户输入数据的验证、前端样式适配如表单控件class、占位符
# 确保用户输入合法且符合业务规则(如邮箱唯一性、密码强度、验证码有效性)
class LoginForm(AuthenticationForm):
"""
前台用户登录表单继承Django内置AuthenticationForm
重写表单控件样式和占位符,适配前端页面布局,提升用户体验
"""
def __init__(self, *args, **kwargs):
"""
初始化登录表单,重写用户名和密码字段的控件配置
"""
super(LoginForm, self).__init__(*args, **kwargs)
# 用户名输入框设置占位符和Bootstrap表单样式类适配前端页面
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
# 密码输入框设置占位符和Bootstrap表单样式类使用密码隐藏控件
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
class RegisterForm(UserCreationForm):
"""
前台用户注册表单继承Django内置UserCreationForm
扩展邮箱字段验证,重写表单控件样式,确保注册数据合法(用户名、邮箱唯一)
"""
def __init__(self, *args, **kwargs):
"""
初始化注册表单,重写用户名、邮箱、密码字段的控件配置
"""
super(RegisterForm, self).__init__(*args, **kwargs)
# 用户名输入框:占位符+Bootstrap样式
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
# 邮箱输入框使用EmailInput控件占位符+Bootstrap样式
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})
# 密码输入框1密码隐藏控件占位符+Bootstrap样式
self.fields['password1'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
# 密码确认框:密码隐藏控件,占位符+Bootstrap样式
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
def clean_email(self):
"""
邮箱字段清洁验证:检查邮箱是否已被注册
若已存在则抛出验证错误,确保邮箱唯一性
"""
email = self.cleaned_data['email'] # 获取经过基础验证的邮箱数据
# 查询数据库,判断该邮箱是否已关联用户
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists")) # 抛出国际化的验证错误
return email # 验证通过,返回邮箱数据
class Meta:
model = get_user_model() # 关联Django当前激活的用户模型此处为BlogUser
fields = ("username", "email") # 注册表单需填写的核心字段:用户名、邮箱(密码字段由父类提供)
class ForgetPasswordForm(forms.Form):
"""
前台用户忘记密码重置表单
包含新密码、密码确认、邮箱、验证码字段,实现密码重置的全流程验证
"""
new_password1 = forms.CharField(
label=_("New password"), # 字段标签(支持国际化)
widget=forms.PasswordInput(
attrs={
"class": "form-control", # Bootstrap表单样式类
'placeholder': _("New password") # 占位符(支持国际化)
}
),
)
new_password2 = forms.CharField(
label="确认密码", # 密码确认字段标签
widget=forms.PasswordInput(
attrs={
"class": "form-control",
'placeholder': _("Confirm password") # 占位符(支持国际化)
}
),
)
email = forms.EmailField(
label='邮箱', # 邮箱字段标签
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': _("Email") # 占位符(支持国际化)
}
),
)
code = forms.CharField(
label=_('Code'), # 验证码字段标签(支持国际化)
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': _("Code") # 占位符(支持国际化)
}
),
)
def clean_new_password2(self):
"""
密码确认字段清洁验证:
1. 检查两次输入的新密码是否一致
2. 验证密码是否符合Django密码强度规则如长度、复杂度
"""
password1 = self.data.get("new_password1") # 获取第一次输入的新密码
password2 = self.data.get("new_password2") # 获取第二次输入的确认密码
# 验证两次密码是否一致
if password1 and password2 and password1 != password2:
raise ValidationError(_("passwords do not match")) # 抛出密码不匹配错误
# 调用Django内置密码验证器检查密码强度
password_validation.validate_password(password2)
return password2 # 验证通过,返回确认密码
def clean_email(self):
"""
邮箱字段清洁验证:检查输入的邮箱是否已注册
若未注册则抛出错误,确保只有已注册用户能重置密码
"""
user_email = self.cleaned_data.get("email") # 获取经过基础验证的邮箱
# 查询数据库判断邮箱是否关联BlogUser
if not BlogUser.objects.filter(email=user_email).exists():
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改(原代码注释保留,提示后续优化隐私保护)
raise ValidationError(_("email does not exist")) # 抛出邮箱未注册错误
return user_email # 验证通过,返回邮箱
def clean_code(self):
"""
验证码字段清洁验证调用utils模块的verify方法验证验证码有效性
若验证码无效(如过期、不匹配)则抛出错误
"""
code = self.cleaned_data.get("code") # 获取用户输入的验证码
# 调用工具函数验证验证码传入邮箱和验证码返回错误信息或None
error = utils.verify(email=self.cleaned_data.get("email"), code=code)
if error: # 若存在错误信息,抛出验证错误
raise ValidationError(error)
return code # 验证通过,返回验证码
class ForgetPasswordCodeForm(forms.Form):
"""
前台用户获取忘记密码验证码的表单
仅包含邮箱字段,用于接收用户邮箱并发送验证码
"""
email = forms.EmailField(
label=_('Email'), # 字段标签(支持国际化)
)