yxt:对accounts的精读和注释

master
yxt 1 month ago
parent 07cc6c4952
commit f30666db7d

@ -1,60 +1,152 @@
"""
Django管理后台用户模型配置模块
本模块配置BlogUser模型在Django管理后台的显示和编辑行为
包括自定义表单验证列表显示字段搜索过滤等功能
主要组件
- BlogUserCreationForm: 用户创建表单处理密码验证和设置
- BlogUserChangeForm: 用户信息修改表单
- BlogUserAdmin: 用户模型管理后台配置类
"""
from django import forms
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.forms import UsernameField
from django.utils.translation import gettext_lazy as _
# Register your models here.
# 导入自定义用户模型
from .models import BlogUser
class BlogUserCreationForm(forms.ModelForm):
"""
博客用户创建表单
扩展自ModelForm专门用于在Django管理后台创建新用户
提供密码确认验证和密码哈希处理功能
"""
# 密码输入字段1 - 使用PasswordInput控件隐藏输入
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
# 密码输入字段2 - 用于密码确认
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
"""表单元数据配置"""
# 指定关联的模型
model = BlogUser
# 指定表单中包含的字段 - 仅包含email字段
fields = ('email',)
def clean_password2(self):
# Check that the two password entries match
"""
密码确认字段验证方法
验证两次输入的密码是否一致确保用户输入正确的密码
Returns:
str: 验证通过的密码
Raises:
forms.ValidationError: 当两次密码输入不一致时抛出验证错误
"""
# 从清洗后的数据中获取两个密码字段的值
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
# 检查两个密码是否存在且相等
if password1 and password2 and password1 != password2:
# 密码不匹配时抛出验证错误
raise forms.ValidationError(_("passwords do not match"))
# 返回验证通过的密码
return password2
def save(self, commit=True):
# Save the provided password in hashed format
"""
表单保存方法
重写保存逻辑处理密码哈希化和设置用户来源
Args:
commit (bool): 是否立即保存到数据库默认为True
Returns:
BlogUser: 保存后的用户实例
"""
# 调用父类保存方法,但不立即提交到数据库
user = super().save(commit=False)
# 使用Django的密码哈希方法设置密码
user.set_password(self.cleaned_data["password1"])
# 如果设置为立即提交,则保存用户并设置来源
if commit:
# 标记用户创建来源为管理后台
user.source = 'adminsite'
# 保存用户到数据库
user.save()
return user
class BlogUserChangeForm(UserChangeForm):
"""
博客用户信息修改表单
继承自Django的UserChangeForm用于在管理后台编辑现有用户信息
保持与Django原生用户管理表单的兼容性
"""
class Meta:
"""表单元数据配置"""
# 指定关联的模型
model = BlogUser
# 包含所有字段
fields = '__all__'
# 指定用户名字段使用Django的UsernameField类型
field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
"""
表单初始化方法
可以在此处添加自定义的表单初始化逻辑
"""
# 调用父类初始化方法
super().__init__(*args, **kwargs)
class BlogUserAdmin(UserAdmin):
"""
博客用户管理后台配置类
继承自Django的UserAdmin自定义BlogUser模型在管理后台的显示和行为
配置列表显示搜索排序等管理界面功能
"""
# 指定用户编辑表单
form = BlogUserChangeForm
# 指定用户创建表单
add_form = BlogUserCreationForm
# 配置列表页面显示的字段
list_display = (
'id',
'nickname',
'username',
'email',
'last_login',
'date_joined',
'source')
'id', # 用户ID
'nickname', # 用户昵称
'username', # 用户名
'email', # 邮箱地址
'last_login', # 最后登录时间
'date_joined', # 注册时间
'source' # 用户来源
)
# 配置列表中可点击跳转到编辑页面的字段
list_display_links = ('id', 'username')
# 配置默认排序规则 - 按ID倒序排列最新的在前
ordering = ('-id',)
search_fields = ('username', 'nickname', 'email')
# 配置搜索框可搜索的字段
search_fields = ('username', 'nickname', 'email')

@ -1,5 +1,37 @@
"""
Django应用配置模块
本模块定义accounts应用的配置类用于配置应用级别的设置和元数据
"""
from django.apps import AppConfig
class AccountsConfig(AppConfig):
"""
用户账户应用配置类
继承自Django的AppConfig类用于配置accounts应用的各项设置
包括应用名称显示名称初始化逻辑等
属性:
name (str): 应用的Python路径标识符Django使用此名称来识别应用
"""
# 应用名称 - 使用Python路径格式Django通过此名称识别应用
# 此名称必须与应用的目录名和INSTALLED_APPS中的配置一致
name = 'accounts'
# 可选应用的可读名称用于Django管理后台显示
# verbose_name = '用户账户'
# 可选:应用初始化方法
# def ready(self):
# """
# 应用初始化完成时调用的方法
#
# 当Django启动完成所有应用加载完毕后会自动调用此方法。
# 常用于信号注册、配置检查等初始化操作。
# """
# # 导入并注册信号处理器
# from . import signals

@ -1,3 +1,15 @@
"""
用户认证表单模块
本模块定义用户相关的Django表单包括
- 用户登录表单
- 用户注册表单
- 密码重置表单
- 验证码表单
所有表单都包含Bootstrap样式类提供一致的用户界面体验
"""
from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
@ -9,109 +21,254 @@ from .models import BlogUser
class LoginForm(AuthenticationForm):
"""
用户登录表单
继承自Django的AuthenticationForm添加Bootstrap样式支持
用于用户通过用户名和密码登录系统
"""
def __init__(self, *args, **kwargs):
"""
初始化表单设置字段的widget属性添加Bootstrap样式
Args:
*args: 可变位置参数
**kwargs: 可变关键字参数
"""
# 调用父类初始化方法
super(LoginForm, self).__init__(*args, **kwargs)
# 设置用户名字段的输入控件和样式
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
attrs={
'placeholder': "username", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
# 设置密码字段的输入控件和样式
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
attrs={
'placeholder': "password", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
class RegisterForm(UserCreationForm):
"""
用户注册表单
继承自Django的UserCreationForm扩展邮箱字段和样式支持
用于新用户注册账号包含用户名邮箱和密码确认功能
"""
def __init__(self, *args, **kwargs):
"""
初始化表单设置所有字段的widget属性添加Bootstrap样式
Args:
*args: 可变位置参数
**kwargs: 可变关键字参数
"""
# 调用父类初始化方法
super(RegisterForm, self).__init__(*args, **kwargs)
# 设置用户名字段的输入控件和样式
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
attrs={
'placeholder': "username", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
# 设置邮箱字段的输入控件和样式
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})
attrs={
'placeholder': "email", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
# 设置密码字段的输入控件和样式
self.fields['password1'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
attrs={
'placeholder': "password", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
# 设置密码确认字段的输入控件和样式
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
attrs={
'placeholder': "repeat password", # 输入框占位符文本
"class": "form-control" # Bootstrap表单控件样式类
})
def clean_email(self):
"""
邮箱字段验证方法
验证邮箱是否已被注册确保邮箱地址的唯一性
Returns:
str: 验证通过的邮箱地址
Raises:
ValidationError: 当邮箱已被注册时抛出验证错误
"""
# 获取清洗后的邮箱数据
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()
# 指定表单中包含的字段
fields = ("username", "email")
class ForgetPasswordForm(forms.Form):
"""
忘记密码重置表单
用于用户通过邮箱和验证码重置密码包含密码强度验证和验证码校验
"""
# 新密码字段1
new_password1 = forms.CharField(
label=_("New password"),
label=_("New password"), # 字段标签
widget=forms.PasswordInput(
attrs={
"class": "form-control",
'placeholder': _("New password")
"class": "form-control", # Bootstrap样式类
'placeholder': _("New password") # 占位符文本
}
),
)
# 新密码字段2 - 用于密码确认
new_password2 = forms.CharField(
label="确认密码",
label="确认密码", # 中文标签
widget=forms.PasswordInput(
attrs={
"class": "form-control",
'placeholder': _("Confirm password")
"class": "form-control", # Bootstrap样式类
'placeholder': _("Confirm password") # 占位符文本
}
),
)
# 邮箱字段 - 用于标识用户和发送验证码
email = forms.EmailField(
label='邮箱',
label='邮箱', # 中文标签
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': _("Email")
'class': 'form-control', # Bootstrap样式类
'placeholder': _("Email") # 占位符文本
}
),
)
# 验证码字段 - 用于验证用户身份
code = forms.CharField(
label=_('Code'),
label=_('Code'), # 字段标签
widget=forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': _("Code")
'class': 'form-control', # Bootstrap样式类
'placeholder': _("Code") # 占位符文本
}
),
)
def clean_new_password2(self):
"""
密码确认字段验证方法
验证两次输入的密码是否一致并检查密码强度
Returns:
str: 验证通过的密码
Raises:
ValidationError: 当密码不匹配或强度不足时抛出验证错误
"""
# 从请求数据中获取两个密码字段的值
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):
"""
邮箱字段验证方法
验证邮箱是否在系统中注册过
Returns:
str: 验证通过的邮箱地址
Raises:
ValidationError: 当邮箱未注册时抛出验证错误
"""
# 获取清洗后的邮箱数据
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
email=user_email
).exists():
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
# 检查邮箱是否存在于用户数据库中
if not BlogUser.objects.filter(email=user_email).exists():
# TODO: 这里会暴露邮箱是否注册的信息,根据安全需求可修改提示
raise ValidationError(_("email does not exist"))
return user_email
def clean_code(self):
"""
验证码字段验证方法
验证邮箱和验证码的匹配关系
Returns:
str: 验证通过的验证码
Raises:
ValidationError: 当验证码无效或过期时抛出验证错误
"""
# 获取清洗后的验证码数据
code = self.cleaned_data.get("code")
# 调用utils模块的verify函数验证验证码
error = utils.verify(
email=self.cleaned_data.get("email"),
code=code,
email=self.cleaned_data.get("email"), # 邮箱地址
code=code, # 验证码
)
# 如果验证返回错误信息,抛出验证错误
if error:
raise ValidationError(error)
return code
class ForgetPasswordCodeForm(forms.Form):
"""
忘记密码验证码请求表单
用于用户请求发送密码重置验证码仅包含邮箱字段
"""
# 邮箱字段 - 用于发送验证码
email = forms.EmailField(
label=_('Email'),
)
label=_('Email'), # 字段标签
# 可以添加widget配置来设置样式
)

@ -25,81 +25,113 @@ class Migration(migrations.Migration):
继承自migrations.Migration定义自定义用户模型的数据库表创建操作
initial = True 表示这是该应用的第一个迁移文件
主要功能
- 创建自定义用户模型BlogUser的数据库表
- 继承Django认证系统的所有基础字段
- 添加博客系统特有的自定义字段
- 设置模型的管理器和配置选项
"""
# 标记为初始迁移文件Django迁移系统会首先执行此文件
initial = True
# 定义迁移依赖关系
dependencies = [
# 声明对Django认证系统组的依赖
# 使用auth应用的0012迁移文件确保用户权限系统正常工作
('auth', '0012_alter_user_first_name_max_length'),
]
# 定义迁移操作序列
operations = [
# 创建博客用户表
# 创建博客用户表的迁移操作
migrations.CreateModel(
# 模型名称 - 对应数据库表名 accounts_bloguser
name='BlogUser',
# 定义模型字段列表
fields=[
# 主键字段 - 使用BigAutoField作为自增主键
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 密码字段 - Django认证系统标准字段存储加密后的密码
('password', models.CharField(max_length=128, verbose_name='password')),
# 最后登录时间字段 - 记录用户最后一次登录的时间
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
# 超级用户标志字段 - 标识用户是否拥有所有权限
('is_superuser', models.BooleanField(default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status')),
# 用户名字段 - 唯一标识用户的字段,包含验证器和错误消息
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'},
help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.',
max_length=150, unique=True,
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
verbose_name='username')),
# 名字字段 - 用户的名字(西方命名习惯)
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
# 姓氏字段 - 用户的姓氏(西方命名习惯)
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
# 邮箱字段 - 用户的电子邮箱地址
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
# 员工状态字段 - 标识用户是否可以登录管理后台
('is_staff', models.BooleanField(default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status')),
# 活跃状态字段 - 标识用户账号是否激活(软删除机制)
('is_active', models.BooleanField(default=True,
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name='active')),
# 注册时间字段 - 记录用户账号创建的时间
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
# 昵称字段 - 博客系统自定义字段,用户显示名称
('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
# 创建时间字段 - 博客系统自定义字段,记录创建时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 最后修改时间字段 - 博客系统自定义字段,记录最后修改时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
# 来源字段 - 博客系统自定义字段记录用户创建来源如注册、OAuth等
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
# 组关联字段 - Django权限系统的组多对多关联
('groups', models.ManyToManyField(blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set', related_query_name='user', to='auth.group',
verbose_name='groups')),
# 权限关联字段 - Django权限系统的用户权限多对多关联
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
related_name='user_set', related_query_name='user',
to='auth.permission', verbose_name='user permissions')),
],
# 模型元数据配置
options={
# 管理后台显示名称(中文)
# 管理后台单数显示名称(中文)
'verbose_name': '用户',
# 管理后台复数显示名称(中文)
'verbose_name_plural': '用户',
# 默认排序规则 - 按ID倒序排列
# 默认排序规则 - 按ID倒序排列(最新的记录在前)
'ordering': ['-id'],
# 指定获取最新记录的字段
# 指定获取最新记录的字段 - 使用id字段确定最新记录
'get_latest_by': 'id',
},
# 定义模型管理器
managers=[
# 使用Django内置的UserManager管理用户对象
# 提供create_user、create_superuser等用户管理方法
('objects', django.contrib.auth.models.UserManager()),
],
),

@ -1,3 +1,14 @@
"""
用户账户应用数据库迁移文件 - 字段优化更新
本迁移文件对初始用户模型进行字段优化和国际化改进
- 重命名时间字段使用更清晰的英文命名
- 更新字段显示名称统一使用英文verbose_name
- 移除冗余字段优化数据库结构
这是对0001_initial迁移的后续更新依赖于初始迁移创建的表结构
"""
# Generated by Django 4.2.5 on 2023-09-06 13:13
from django.db import migrations, models
@ -5,42 +16,85 @@ import django.utils.timezone
class Migration(migrations.Migration):
"""
用户模型字段优化迁移类
对BlogUser模型进行字段级别的优化和改进
- 标准化字段命名约定
- 改进国际化支持
- 优化时间字段的语义清晰度
此迁移依赖于accounts应用的0001_initial迁移文件
"""
# 定义迁移依赖关系 - 依赖于本应用的初始迁移
dependencies = [
# 依赖accounts应用的第一个迁移文件确保BlogUser表已创建
('accounts', '0001_initial'),
]
# 定义迁移操作序列 - 按顺序执行以下数据库变更
operations = [
# 修改模型选项 - 更新管理后台显示名称
migrations.AlterModelOptions(
name='bloguser',
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
options={
# 指定获取最新记录的字段 - 保持使用id字段
'get_latest_by': 'id',
# 保持默认排序规则 - 按ID倒序排列
'ordering': ['-id'],
# 更新单数显示名称为英文
'verbose_name': 'user',
# 更新复数显示名称为英文
'verbose_name_plural': 'user'
},
),
# 移除字段 - 删除created_time字段
# 该字段功能被creation_time字段替代
migrations.RemoveField(
model_name='bloguser',
name='created_time',
),
# 移除字段 - 删除last_mod_time字段
# 该字段功能被last_modify_time字段替代
migrations.RemoveField(
model_name='bloguser',
name='last_mod_time',
),
# 添加新字段 - 创建时间字段(新命名)
migrations.AddField(
model_name='bloguser',
name='creation_time',
# 使用DateTimeField存储完整的时间戳
# default参数使用Django的时区感知当前时间
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
# 添加新字段 - 最后修改时间字段(新命名)
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
# 使用DateTimeField存储完整的时间戳
# default参数使用Django的时区感知当前时间
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
# 修改字段选项 - 更新昵称字段的显示名称
migrations.AlterField(
model_name='bloguser',
name='nickname',
# 保持字段类型和约束不变仅更新verbose_name为英文
field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
),
# 修改字段选项 - 更新来源字段的显示名称
migrations.AlterField(
model_name='bloguser',
name='source',
# 保持字段类型和约束不变仅更新verbose_name为英文
field=models.CharField(blank=True, max_length=100, verbose_name='create source'),
),
]
]

@ -1,3 +1,10 @@
"""
自定义用户模型模块
本模块定义博客系统的自定义用户模型BlogUser扩展Django内置的AbstractUser模型
添加博客系统特有的用户字段和方法
"""
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
@ -6,30 +13,113 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import get_current_site
# Create your models here.
class BlogUser(AbstractUser):
nickname = models.CharField(_('nick name'), max_length=100, blank=True)
creation_time = models.DateTimeField(_('creation time'), default=now)
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
source = models.CharField(_('create source'), max_length=100, blank=True)
"""
博客系统自定义用户模型
继承自Django的AbstractUser在标准用户模型基础上添加博客系统特有的字段
- 昵称字段
- 创建时间字段
- 最后修改时间字段
- 用户来源字段
同时提供获取用户相关URL的便捷方法
"""
# 昵称字段 - 用户的显示名称,可以为空
nickname = models.CharField(
_('nick name'), # 字段显示名称(支持国际化)
max_length=100, # 最大长度100字符
blank=True # 允许为空(非必填字段)
)
# 创建时间字段 - 记录用户账号创建的时间
creation_time = models.DateTimeField(
_('creation time'), # 字段显示名称(支持国际化)
default=now # 默认值为当前时间
)
# 最后修改时间字段 - 记录用户信息最后修改的时间
last_modify_time = models.DateTimeField(
_('last modify time'), # 字段显示名称(支持国际化)
default=now # 默认值为当前时间
)
# 用户来源字段 - 记录用户账号的创建来源
source = models.CharField(
_('create source'), # 字段显示名称(支持国际化)
max_length=100, # 最大长度100字符
blank=True # 允许为空(非必填字段)
)
def get_absolute_url(self):
"""
获取用户的绝对URL相对路径
用于Django的通用视图和模板中生成用户详情页链接
Returns:
str: 用户详情页的URL路径
Example:
>>> user.get_absolute_url()
'/author/admin/'
"""
# 使用reverse函数通过URL名称和参数生成URL路径
return reverse(
'blog:author_detail', kwargs={
'author_name': self.username})
'blog:author_detail', # URL配置的名称
kwargs={
'author_name': self.username # URL参数作者用户名
})
def __str__(self):
"""
对象字符串表示方法
定义模型实例在Django管理后台和shell中的显示内容
Returns:
str: 用户的邮箱地址
"""
return self.email
def get_full_url(self):
"""
获取用户的完整URL包含域名
生成包含协议和域名的完整用户详情页URL用于外部链接
Returns:
str: 完整的用户详情页URL
Example:
>>> user.get_full_url()
'https://example.com/author/admin/'
"""
# 获取当前站点的域名
site = get_current_site().domain
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
# 生成完整的URL包含HTTPS协议和域名
url = "https://{site}{path}".format(
site=site, # 站点域名
path=self.get_absolute_url() # 相对路径
)
return url
class Meta:
"""
模型元数据配置类
定义模型的数据库表配置和Django管理后台显示选项
"""
# 默认排序规则 - 按ID倒序排列最新的记录在前
ordering = ['-id']
# 管理后台单数显示名称(支持国际化)
verbose_name = _('user')
# 管理后台复数显示名称 - 使用与单数相同的名称
verbose_name_plural = verbose_name
get_latest_by = 'id'
# 指定获取最新记录的字段 - 使用id字段确定最新记录1
get_latest_by = 'id'

@ -1,3 +1,10 @@
"""
用户账户应用测试模块
本模块包含用户账户相关的所有测试用例覆盖用户注册登录密码重置
邮箱验证等核心功能的测试
"""
from django.test import Client, RequestFactory, TestCase
from django.urls import reverse
from django.utils import timezone
@ -9,128 +16,203 @@ from djangoblog.utils import *
from . import utils
# Create your tests here.
class AccountTest(TestCase):
"""
用户账户功能测试类
测试用户账户相关的所有功能包括
- 用户认证和登录
- 用户注册流程
- 邮箱验证码功能
- 密码重置流程
- 权限访问控制
"""
def setUp(self):
"""
测试初始化方法
在每个测试方法执行前运行创建测试所需的初始数据和环境
"""
# 创建测试客户端用于模拟HTTP请求
self.client = Client()
# 创建请求工厂,用于构建请求对象
self.factory = RequestFactory()
# 创建测试用户
self.blog_user = BlogUser.objects.create_user(
username="test",
email="admin@admin.com",
password="12345678"
)
# 设置测试用的新密码
self.new_test = "xxx123--="
def test_validate_account(self):
"""
测试账户验证和权限功能
验证超级用户的创建登录管理后台访问和文章管理权限
"""
# 创建超级用户
site = get_current_site().domain
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="qwer!@#$ggg")
# 从数据库获取刚创建的用户
testuser = BlogUser.objects.get(username='liangliangyy1')
# 测试用户登录功能
loginresult = self.client.login(
username='liangliangyy1',
password='qwer!@#$ggg')
# 断言登录成功
self.assertEqual(loginresult, True)
# 测试管理后台访问权限
response = self.client.get('/admin/')
self.assertEqual(response.status_code, 200)
# 创建测试分类
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
# 创建测试文章
article = Article()
article.title = "nicetitleaaa"
article.body = "nicecontentaaa"
article.author = user
article.category = category
article.type = 'a'
article.status = 'p'
article.type = 'a' # 文章类型
article.status = 'p' # 发布状态
article.save()
# 测试文章管理页面访问权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
def test_validate_register(self):
"""
测试用户注册完整流程
验证用户注册邮箱验证登录权限提升和文章管理的完整流程
"""
# 验证注册前用户不存在
self.assertEquals(
0, len(
BlogUser.objects.filter(
email='user123@user.com')))
# 提交用户注册表单
response = self.client.post(reverse('account:register'), {
'username': 'user1233',
'email': 'user123@user.com',
'password1': 'password123!q@wE#R$T',
'password2': 'password123!q@wE#R$T',
})
# 验证用户已成功创建
self.assertEquals(
1, len(
BlogUser.objects.filter(
email='user123@user.com')))
# 获取新创建的用户
user = BlogUser.objects.filter(email='user123@user.com')[0]
# 生成邮箱验证签名
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
path = reverse('accounts:result')
url = '{path}?type=validation&id={id}&sign={sign}'.format(
path=path, id=user.id, sign=sign)
# 访问验证结果页面
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
# 测试用户登录
self.client.login(username='user1233', password='password123!q@wE#R$T')
# 提升用户权限为超级用户
user = BlogUser.objects.filter(email='user123@user.com')[0]
user.is_superuser = True
user.is_staff = True
user.save()
# 清理侧边栏缓存
delete_sidebar_cache()
# 创建测试分类
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
# 创建测试文章
article = Article()
article.category = category
article.title = "nicetitle333"
article.body = "nicecontentttt"
article.author = user
article.type = 'a'
article.status = 'p'
article.type = 'a' # 文章类型
article.status = 'p' # 发布状态
article.save()
# 测试文章管理页面访问
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
# 测试用户登出
response = self.client.get(reverse('account:logout'))
self.assertIn(response.status_code, [301, 302, 200])
# 验证登出后无法访问管理页面
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
# 测试错误密码登录
response = self.client.post(reverse('account:login'), {
'username': 'user1233',
'password': 'password123'
'password': 'password123' # 错误密码
})
self.assertIn(response.status_code, [301, 302, 200])
# 验证错误密码登录后仍无法访问管理页面
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
def test_verify_email_code(self):
"""
测试邮箱验证码功能
验证验证码的生成发送存储和验证流程
"""
to_email = "admin@admin.com"
# 生成验证码
code = generate_code()
# 存储验证码
utils.set_code(to_email, code)
# 发送验证邮件
utils.send_verify_email(to_email, code)
# 测试正确验证码验证
err = utils.verify("admin@admin.com", code)
self.assertEqual(err, None)
self.assertEqual(err, None) # 验证成功应返回None
# 测试错误邮箱验证
err = utils.verify("admin@123.com", code)
self.assertEqual(type(err), str)
self.assertEqual(type(err), str) # 验证失败应返回错误信息字符串
def test_forget_password_email_code_success(self):
"""
测试忘记密码验证码请求成功场景
验证正确邮箱地址的验证码请求处理
"""
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict(email="admin@admin.com")
@ -140,32 +222,49 @@ class AccountTest(TestCase):
self.assertEqual(resp.content.decode("utf-8"), "ok")
def test_forget_password_email_code_fail(self):
"""
测试忘记密码验证码请求失败场景
验证空邮箱和错误格式邮箱的请求处理
"""
# 测试空邮箱提交
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict()
data=dict() # 空数据
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
# 测试错误格式邮箱提交
resp = self.client.post(
path=reverse("account:forget_password_code"),
data=dict(email="admin@com")
data=dict(email="admin@com") # 无效邮箱格式
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
def test_forget_password_email_success(self):
"""
测试忘记密码重置成功场景
验证正确的验证码和密码重置流程
"""
# 生成并设置验证码
code = generate_code()
utils.set_code(self.blog_user.email, code)
# 准备密码重置数据
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
code=code,
)
# 提交密码重置请求
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 302)
self.assertEqual(resp.status_code, 302) # 重定向响应
# 验证用户密码是否修改成功
blog_user = BlogUser.objects.filter(
@ -175,10 +274,15 @@ class AccountTest(TestCase):
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
def test_forget_password_email_not_user(self):
"""
测试不存在的用户密码重置
验证对不存在用户的密码重置请求处理
"""
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email="123@123.com",
email="123@123.com", # 不存在的邮箱
code="123456",
)
resp = self.client.post(
@ -186,22 +290,25 @@ class AccountTest(TestCase):
data=data
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.status_code, 200) # 应返回表单错误页面
def test_forget_password_email_code_error(self):
"""
测试错误验证码的密码重置
验证错误验证码的密码重置请求处理
"""
code = generate_code()
utils.set_code(self.blog_user.email, code)
data = dict(
new_password1=self.new_test,
new_password2=self.new_test,
email=self.blog_user.email,
code="111111",
code="111111", # 错误的验证码
)
resp = self.client.post(
path=reverse("account:forget_password"),
data=data
)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.status_code, 200) # 应返回表单错误页面

@ -1,28 +1,60 @@
"""
用户账户应用URL配置模块
本模块定义用户账户相关的所有URL路由包括登录注册登出
密码重置等用户认证相关的端点
URL模式使用正则表达式和路径转换器来匹配不同的用户操作请求
"""
from django.urls import path
from django.urls import re_path
# 导入视图模块
from . import views
# 导入自定义登录表单
from .forms import LoginForm
# 应用命名空间用于URL反向解析
app_name = "accounts"
urlpatterns = [re_path(r'^login/$',
views.LoginView.as_view(success_url='/'),
name='login',
kwargs={'authentication_form': LoginForm}),
re_path(r'^register/$',
views.RegisterView.as_view(success_url="/"),
name='register'),
re_path(r'^logout/$',
views.LogoutView.as_view(),
name='logout'),
path(r'account/result.html',
views.account_result,
name='result'),
re_path(r'^forget_password/$',
views.ForgetPasswordView.as_view(),
name='forget_password'),
re_path(r'^forget_password_code/$',
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'),
]
# URL模式列表定义请求URL与视图的映射关系
urlpatterns = [
# 用户登录URL
re_path(r'^login/$', # 匹配 /login/ 路径
# 使用类视图设置登录成功后的重定向URL为首页
views.LoginView.as_view(success_url='/'),
name='login', # URL名称用于反向解析
# 传递额外参数,指定使用自定义登录表单
kwargs={'authentication_form': LoginForm}),
# 用户注册URL
re_path(r'^register/$', # 匹配 /register/ 路径
# 使用类视图设置注册成功后的重定向URL为首页
views.RegisterView.as_view(success_url="/"),
name='register'), # URL名称用于反向解析
# 用户登出URL
re_path(r'^logout/$', # 匹配 /logout/ 路径
# 使用类视图,处理用户登出逻辑
views.LogoutView.as_view(),
name='logout'), # URL名称用于反向解析
# 账户操作结果页面URL
path(r'account/result.html', # 匹配 /account/result.html 路径
# 使用函数视图,显示账户操作结果(如注册成功、验证结果等)
views.account_result,
name='result'), # URL名称用于反向解析
# 忘记密码页面URL
re_path(r'^forget_password/$', # 匹配 /forget_password/ 路径
# 使用类视图,处理密码重置请求
views.ForgetPasswordView.as_view(),
name='forget_password'), # URL名称用于反向解析
# 忘记密码验证码请求URL
re_path(r'^forget_password_code/$', # 匹配 /forget_password_code/ 路径
# 使用类视图,处理发送密码重置验证码的请求
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'), # URL名称用于反向解析
]

@ -1,26 +1,91 @@
"""
自定义用户认证后端模块
本模块提供扩展的用户认证功能支持使用用户名或邮箱进行登录
扩展了Django标准的ModelBackend认证后端
"""
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class EmailOrUsernameModelBackend(ModelBackend):
"""
允许使用用户名或邮箱登录
自定义用户认证后端 - 支持用户名或邮箱登录
继承自Django的ModelBackend扩展认证功能
- 允许用户使用用户名或邮箱地址进行登录
- 自动检测输入的是用户名还是邮箱格式
- 保持与Django原生认证系统的兼容性
使用场景
当用户输入包含'@'符号时系统将其识别为邮箱进行认证
否则将其识别为用户名进行认证
"""
def authenticate(self, request, username=None, password=None, **kwargs):
"""
用户认证方法
重写认证逻辑支持通过用户名或邮箱进行用户身份验证
Args:
request: HttpRequest对象包含请求信息
username (str): 用户输入的用户名或邮箱地址
password (str): 用户输入的密码
**kwargs: 其他关键字参数
Returns:
User: 认证成功的用户对象
None: 认证失败返回None
Example:
>>> backend = EmailOrUsernameModelBackend()
>>> # 使用用户名认证
>>> user = backend.authenticate(request, username='admin', password='password')
>>> # 使用邮箱认证
>>> user = backend.authenticate(request, username='admin@example.com', password='password')
"""
# 判断输入的是邮箱还是用户名
if '@' in username:
# 如果包含'@'符号,按邮箱处理
kwargs = {'email': username}
else:
# 否则按用户名处理
kwargs = {'username': username}
try:
# 根据用户名或邮箱查找用户
user = get_user_model().objects.get(**kwargs)
# 验证密码是否正确
if user.check_password(password):
# 密码验证成功,返回用户对象
return user
except get_user_model().DoesNotExist:
# 用户不存在返回None表示认证失败
return None
def get_user(self, username):
"""
根据用户ID获取用户对象
重写用户获取方法通过用户ID主键获取用户实例
Args:
username (int/str): 用户的ID主键值
Returns:
User: 对应的用户对象
None: 用户不存在时返回None
Note:
这里的参数名username实际上是用户ID这是为了保持与父类接口一致
"""
try:
# 根据主键用户ID查找用户
return get_user_model().objects.get(pk=username)
except get_user_model().DoesNotExist:
return None
# 用户不存在返回None
return None

@ -1,3 +1,15 @@
"""
邮箱验证码工具模块
本模块提供邮箱验证码的生成发送验证和缓存管理功能
用于用户注册密码重置等需要邮箱验证的场景
主要功能
- 发送验证码邮件
- 验证码的存储和读取
- 验证码有效性验证
"""
import typing
from datetime import timedelta
@ -7,43 +19,109 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import send_email
# 验证码有效期配置 - 5分钟
_code_ttl = timedelta(minutes=5)
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
"""发送重设密码验证码
"""
发送验证码邮件
向指定邮箱发送包含验证码的邮件用于用户身份验证
Args:
to_mail: 接受邮箱
subject: 邮件主题
code: 验证码
to_mail (str): 接收邮件的邮箱地址
code (str): 要发送的验证码
subject (str): 邮件主题默认为"Verify Email"
Example:
>>> send_verify_email("user@example.com", "123456")
# 向user@example.com发送验证码123456
"""
# 构建邮件HTML内容包含验证码信息
html_content = _(
"You are resetting the password, the verification code is%(code)s, valid within 5 minutes, please keep it "
"properly") % {'code': code}
# 调用邮件发送工具发送邮件
send_email([to_mail], subject, html_content)
def verify(email: str, code: str) -> typing.Optional[str]:
"""验证code是否有效
"""
验证验证码是否有效
检查用户输入的验证码与缓存中存储的是否一致并验证有效性
Args:
email: 请求邮箱
code: 验证码
Return:
如果有错误就返回错误str
Node:
这里的错误处理不太合理应该采用raise抛出
否测调用方也需要对error进行处理
email (str): 用户邮箱地址作为缓存键
code (str): 用户输入的验证码
Returns:
typing.Optional[str]:
- None: 验证码正确且有效
- str: 错误信息字符串验证码错误或无效
Note:
当前错误处理方式不够合理应该使用异常抛出机制
这样调用方可以通过try-except处理错误而不是检查返回值
Example:
>>> result = verify("user@example.com", "123456")
>>> if result:
>>> print(f"验证失败: {result}")
>>> else:
>>> print("验证成功")
"""
# 从缓存中获取该邮箱对应的验证码
cache_code = get_code(email)
# 比较用户输入的验证码与缓存中的验证码
if cache_code != code:
# 验证码不匹配,返回错误信息
return gettext("Verification code error")
# 验证成功返回None
def set_code(email: str, code: str):
"""设置code"""
"""
设置验证码到缓存
将验证码存储到Django缓存系统中并设置有效期
Args:
email (str): 邮箱地址作为缓存键
code (str): 要存储的验证码
Example:
>>> set_code("user@example.com", "123456")
# 将验证码123456存储到缓存键为"user@example.com"
"""
# 使用Django缓存系统存储验证码设置5分钟有效期
cache.set(email, code, _code_ttl.seconds)
def get_code(email: str) -> typing.Optional[str]:
"""获取code"""
return cache.get(email)
"""
从缓存中获取验证码
根据邮箱地址从缓存中获取对应的验证码
Args:
email (str): 邮箱地址作为缓存键
Returns:
typing.Optional[str]:
- str: 找到的验证码
- None: 验证码不存在或已过期
Example:
>>> code = get_code("user@example.com")
>>> if code:
>>> print(f"验证码是: {code}")
>>> else:
>>> print("验证码不存在或已过期")
"""
# 从Django缓存系统中获取验证码
return cache.get(email)

@ -1,3 +1,15 @@
"""
用户账户视图模块
本模块包含用户账户相关的所有视图处理逻辑包括
- 用户注册登录登出
- 邮箱验证
- 密码重置
- 验证码发送
使用类视图和函数视图结合的方式处理用户认证流程
"""
import logging
from django.utils.translation import gettext_lazy as _
from django.conf import settings
@ -26,34 +38,68 @@ from . import utils
from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
from .models import BlogUser
# 配置日志记录器
logger = logging.getLogger(__name__)
# Create your views here.
class RegisterView(FormView):
"""
用户注册视图
处理新用户注册流程包括表单验证用户创建邮箱验证邮件发送等
"""
# 指定使用的表单类
form_class = RegisterForm
# 指定注册页面模板
template_name = 'account/registration_form.html'
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
"""
请求分发方法添加CSRF保护装饰器
确保注册请求受到CSRF保护防止跨站请求伪造攻击
"""
return super(RegisterView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
"""
表单验证通过后的处理逻辑
创建新用户发送验证邮件重定向到结果页面
Args:
form: 验证通过的注册表单实例
Returns:
HttpResponseRedirect: 重定向到结果页面
"""
if form.is_valid():
# 创建用户但不立即保存到数据库
user = form.save(False)
# 设置用户为非激活状态,等待邮箱验证
user.is_active = False
# 记录用户来源为注册页面
user.source = 'Register'
# 保存用户到数据库
user.save(True)
# 获取当前站点域名
site = get_current_site().domain
# 生成邮箱验证签名
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
# 调试模式下使用本地地址
if settings.DEBUG:
site = '127.0.0.1:8000'
# 构建验证URL
path = reverse('account:result')
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
site=site, path=path, id=user.id, sign=sign)
# 构建邮件内容
content = """
<p>请点击下面链接验证您的邮箱</p>
@ -64,6 +110,8 @@ class RegisterView(FormView):
如果上面链接无法打开请将此链接复制至浏览器
{url}
""".format(url=url)
# 发送验证邮件
send_email(
emailto=[
user.email,
@ -71,43 +119,88 @@ class RegisterView(FormView):
title='验证您的电子邮箱',
content=content)
# 重定向到注册结果页面
url = reverse('accounts:result') + \
'?type=register&id=' + str(user.id)
return HttpResponseRedirect(url)
else:
# 表单验证失败,重新渲染表单页面
return self.render_to_response({
'form': form
})
class LogoutView(RedirectView):
"""
用户登出视图
处理用户登出逻辑清理会话和缓存
"""
# 登出后重定向到的URL
url = '/login/'
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
"""
请求分发方法添加不缓存装饰器
确保登出页面不会被浏览器缓存
"""
return super(LogoutView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
"""
处理GET请求的登出逻辑
执行用户登出操作清理侧边栏缓存
"""
# 执行用户登出
logout(request)
# 清理侧边栏缓存
delete_sidebar_cache()
# 调用父类方法进行重定向
return super(LogoutView, self).get(request, *args, **kwargs)
class LoginView(FormView):
"""
用户登录视图
处理用户登录认证支持记住登录状态功能
"""
# 指定使用的表单类
form_class = LoginForm
# 指定登录页面模板
template_name = 'account/login.html'
# 登录成功后的默认重定向URL
success_url = '/'
# 重定向字段名称
redirect_field_name = REDIRECT_FIELD_NAME
login_ttl = 2626560 # 一个月的时间
# 记住登录状态的会话有效期(一个月)
login_ttl = 2626560
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
"""
请求分发方法添加安全装饰器
- sensitive_post_parameters: 保护密码参数
- csrf_protect: CSRF保护
- never_cache: 禁止缓存
"""
return super(LoginView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""
获取模板上下文数据
添加重定向URL到上下文
"""
# 从GET参数获取重定向URL
redirect_to = self.request.GET.get(self.redirect_field_name)
if redirect_to is None:
redirect_to = '/'
@ -116,25 +209,43 @@ class LoginView(FormView):
return super(LoginView, self).get_context_data(**kwargs)
def form_valid(self, form):
"""
表单验证通过后的处理逻辑
执行用户登录认证处理记住登录状态
"""
# 使用Django认证表单进行验证
form = AuthenticationForm(data=self.request.POST, request=self.request)
if form.is_valid():
# 清理侧边栏缓存
delete_sidebar_cache()
logger.info(self.redirect_field_name)
# 执行用户登录
auth.login(self.request, form.get_user())
# 处理记住登录状态
if self.request.POST.get("remember"):
self.request.session.set_expiry(self.login_ttl)
return super(LoginView, self).form_valid(form)
# return HttpResponseRedirect('/')
else:
# 登录失败,重新渲染登录页面
return self.render_to_response({
'form': form
})
def get_success_url(self):
"""
获取登录成功后的重定向URL
验证重定向URL的安全性防止开放重定向攻击
"""
# 从POST数据获取重定向URL
redirect_to = self.request.POST.get(self.redirect_field_name)
# 验证URL是否安全同源策略
if not url_has_allowed_host_and_scheme(
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
@ -143,62 +254,124 @@ class LoginView(FormView):
def account_result(request):
"""
账户操作结果页面视图
显示注册结果或处理邮箱验证
Args:
request: HTTP请求对象
Returns:
HttpResponse: 结果页面响应
"""
# 获取操作类型和用户ID
type = request.GET.get('type')
id = request.GET.get('id')
# 获取用户对象不存在则返回404
user = get_object_or_404(get_user_model(), id=id)
logger.info(type)
# 如果用户已激活,重定向到首页
if user.is_active:
return HttpResponseRedirect('/')
# 处理注册和验证类型
if type and type in ['register', 'validation']:
if type == 'register':
# 注册成功页面
content = '''
恭喜您注册成功一封验证邮件已经发送到您的邮箱请验证您的邮箱后登录本站
'''
title = '注册成功'
else:
# 邮箱验证处理
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
sign = request.GET.get('sign')
# 验证签名
if sign != c_sign:
return HttpResponseForbidden()
# 激活用户账号
user.is_active = True
user.save()
content = '''
恭喜您已经成功的完成邮箱验证您现在可以使用您的账号来登录本站
'''
title = '验证成功'
# 渲染结果页面
return render(request, 'account/result.html', {
'title': title,
'content': content
})
else:
# 无效类型,重定向到首页
return HttpResponseRedirect('/')
class ForgetPasswordView(FormView):
"""
忘记密码重置视图
处理用户密码重置请求验证验证码并更新密码
"""
# 指定使用的表单类
form_class = ForgetPasswordForm
# 指定模板名称
template_name = 'account/forget_password.html'
def form_valid(self, form):
"""
表单验证通过后的处理逻辑
重置用户密码并重定向到登录页面
"""
if form.is_valid():
# 根据邮箱查找用户
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
# 使用新密码的哈希值更新用户密码
blog_user.password = make_password(form.cleaned_data["new_password2"])
# 保存用户信息
blog_user.save()
# 重定向到登录页面
return HttpResponseRedirect('/login/')
else:
# 表单验证失败,重新渲染表单
return self.render_to_response({'form': form})
class ForgetPasswordEmailCode(View):
"""
忘记密码验证码发送视图
处理密码重置验证码的发送请求
"""
def post(self, request: HttpRequest):
"""
处理POST请求发送密码重置验证码
Args:
request: HTTP请求对象
Returns:
HttpResponse: 操作结果响应
"""
# 验证表单数据
form = ForgetPasswordCodeForm(request.POST)
if not form.is_valid():
return HttpResponse("错误的邮箱")
# 获取邮箱地址
to_email = form.cleaned_data["email"]
# 生成验证码
code = generate_code()
# 发送验证邮件
utils.send_verify_email(to_email, code)
# 存储验证码到缓存
utils.set_code(to_email, code)
return HttpResponse("ok")
return HttpResponse("ok")
Loading…
Cancel
Save