|
|
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'), # 字段标签(支持国际化)
|
|
|
) |