第七、八周注释代码

wty_branch
吴庭玉 6 months ago
parent 22122d978a
commit 52e177128e

@ -1,49 +1,138 @@
#wty Django 管理后台配置文件,用于自定义 BlogUser 模型在 Admin 站点的显示和行为
#wty 提供用户创建、编辑表单和管理界面的自定义配置
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.
#wty 导入自定义的用户模型
from .models import BlogUser
#wty 自定义用户创建表单,用于管理员在后台创建新用户
class BlogUserCreationForm(forms.ModelForm):
#wty 密码输入字段1使用密码输入控件进行安全输入
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
#wty 密码确认字段2用于验证两次密码输入的一致性
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
#wty 指定关联的模型为 BlogUser
model = BlogUser
#wty 表单字段列表,创建用户时只显示邮箱字段
fields = ('email',)
#wty 密码验证方法,确保两次输入的密码一致
def clean_password2(self):
# Check that the two password entries match
#wty 获取表单中两次输入的密码值
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
#wty 检查密码是否一致,不一致则抛出验证错误
if password1 and password2 and password1 != password2:
raise forms.ValidationError(_("passwords do not match"))
#wty 返回验证通过的密码
return password2
#wty 保存用户方法,对密码进行哈希处理并设置用户来源
def save(self, commit=True):
# Save the provided password in hashed format
#wty 先创建用户实例但不立即保存到数据库
user = super().save(commit=False)
#wty 使用 Django 的安全密码哈希方法设置密码
user.set_password(self.cleaned_data["password1"])
#wty 如果 commit 为 True则保存用户到数据库
if commit:
#wty 标记用户来源为管理员后台创建
user.source = 'adminsite'
#wty 保存用户到数据库
user.save()
#wty 返回保存的用户对象
return user
#wty 自定义用户修改表单,用于管理员在后台编辑用户信息
class BlogUserChangeForm(UserChangeForm):
class Meta:
#wty 指定关联的模型为 BlogUser
model = BlogUser
#wty 包含所有字段,允许编辑用户的所有信息
fields = '__all__'
#wty 指定用户名字段使用 Django 的 UsernameField 类型进行验证
field_classes = {'username': UsernameField}
#wty 初始化方法,可以在此添加表单字段的自定义逻辑
def __init__(self, *args, **kwargs):
#wty 调用父类的初始化方法
super().__init__(*args, **kwargs)
#wty 自定义用户管理类,配置用户在 Admin 后台的显示方式和管理功能
class BlogUserAdmin(UserAdmin):
#wty 指定修改用户信息时使用的表单类
form = BlogUserChangeForm
#wty 指定添加新用户时使用的表单类
add_form = BlogUserCreationForm
#wty 定义用户列表页面显示的字段列
list_display = (
'id', #wty 用户ID作为主键标识
'nickname', #wty 昵称,用户的显示名称
'username', #wty 用户名,用于登录的唯一标识
'email', #wty 邮箱地址,用户联系方式
'last_login', #wty 最后登录时间,记录用户活跃度
'date_joined', #wty 注册时间,用户创建时间
'source' #wty 用户来源,记录注册渠道
)
#wty 定义列表中可点击跳转到用户详情页的字段
list_display_links = ('id', 'username')
#wty 定义用户列表的默认排序规则按ID倒序排列最新用户显示在最前面
ordering = ('-id',)
#wty 以下为重复代码块,实际应用中应删除重复部分
#wty 重复的导入语句和类定义(应保留一份即可)
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 _
#wty 重复的用户创建表单类定义
class BlogUserCreationForm(forms.ModelForm):
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
model = BlogUser
fields = ('email',)
def clean_password2(self):
#wty 重复的密码验证逻辑
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):
#wty 重复的用户保存逻辑
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.source = 'adminsite'
user.save()
return user
#wty 重复的用户修改表单类定义
class BlogUserChangeForm(UserChangeForm):
class Meta:
model = BlogUser
fields = '__all__'
field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#wty 重复的用户管理类定义
class BlogUserAdmin(UserAdmin):
form = BlogUserChangeForm
add_form = BlogUserCreationForm
@ -57,3 +146,4 @@ class BlogUserAdmin(UserAdmin):
'source')
list_display_links = ('id', 'username')
ordering = ('-id',)

@ -1,5 +1,10 @@
#wty accounts 应用配置类文件
#wty 定义应用的配置信息、元数据和启动行为
from django.apps import AppConfig
#wty 自定义用户账户应用的配置类
class AccountsConfig(AppConfig):
name = 'accounts'
#wty 应用名称,使用 Python 路径格式(例如 'accounts'
name = 'accounts'

@ -1,3 +1,6 @@
#wty 用户认证相关表单定义文件
#wty 包含登录、注册、忘记密码等用户认证功能的表单类
from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
@ -8,40 +11,58 @@ from . import utils
from .models import BlogUser
#wty 用户登录表单,继承自 Django 的 AuthenticationForm
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
#wty 调用父类初始化方法
super(LoginForm, self).__init__(*args, **kwargs)
#wty 自定义用户名字段的 widget添加占位符和 CSS 类
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
#wty 自定义密码字段的 widget添加占位符和 CSS 类
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
#wty 用户注册表单,继承自 Django 的 UserCreationForm
class RegisterForm(UserCreationForm):
def __init__(self, *args, **kwargs):
#wty 调用父类初始化方法
super(RegisterForm, self).__init__(*args, **kwargs)
#wty 自定义用户名字段的 widget
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
#wty 自定义邮箱字段的 widget
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})
#wty 自定义密码字段的 widget
self.fields['password1'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
#wty 自定义密码确认字段的 widget
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
#wty 邮箱验证方法,检查邮箱是否已存在
def clean_email(self):
#wty 获取表单提交的邮箱
email = self.cleaned_data['email']
#wty 检查邮箱是否已被注册
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists"))
#wty 返回验证通过的邮箱
return email
class Meta:
#wty 指定关联的用户模型
model = get_user_model()
#wty 表单包含的字段:用户名和邮箱
fields = ("username", "email")
#wty 忘记密码表单,用于重置密码
class ForgetPasswordForm(forms.Form):
#wty 新密码字段
new_password1 = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(
@ -52,6 +73,7 @@ class ForgetPasswordForm(forms.Form):
),
)
#wty 确认密码字段
new_password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(
@ -62,6 +84,7 @@ class ForgetPasswordForm(forms.Form):
),
)
#wty 邮箱字段,用于验证用户身份
email = forms.EmailField(
label='邮箱',
widget=forms.TextInput(
@ -72,6 +95,7 @@ class ForgetPasswordForm(forms.Form):
),
)
#wty 验证码字段,用于二次验证
code = forms.CharField(
label=_('Code'),
widget=forms.TextInput(
@ -82,36 +106,51 @@ class ForgetPasswordForm(forms.Form):
),
)
#wty 验证两次输入的密码是否一致
def clean_new_password2(self):
#wty 获取两次输入的密码
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
#wty 检查密码是否一致
if password1 and password2 and password1 != password2:
raise ValidationError(_("passwords do not match"))
#wty 使用 Django 的密码验证器验证密码强度
password_validation.validate_password(password2)
#wty 返回验证通过的密码
return password2
#wty 验证邮箱是否存在
def clean_email(self):
#wty 获取表单提交的邮箱
user_email = self.cleaned_data.get("email")
#wty 检查邮箱是否在数据库中存在
if not BlogUser.objects.filter(
email=user_email
).exists():
# todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
#wty 如果邮箱不存在,抛出验证错误
raise ValidationError(_("email does not exist"))
#wty 返回验证通过的邮箱
return user_email
#wty 验证验证码是否正确
def clean_code(self):
#wty 获取表单提交的验证码
code = self.cleaned_data.get("code")
#wty 调用工具函数验证验证码
error = utils.verify(
email=self.cleaned_data.get("email"),
code=code,
)
#wty 如果验证失败,抛出错误
if error:
raise ValidationError(error)
#wty 返回验证通过的验证码
return code
#wty 忘记密码验证码表单,用于请求发送验证码
class ForgetPasswordCodeForm(forms.Form):
#wty 邮箱字段,用于接收需要重置密码的邮箱地址
email = forms.EmailField(
label=_('Email'),
)
)

@ -7,43 +7,65 @@ import django.utils.timezone
class Migration(migrations.Migration):
#wty 标记为初始迁移
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
#wty 依赖的迁移文件,这里依赖 Django auth 应用的迁移
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='BlogUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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='修改时间')),
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
('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')),
('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': '用户',
'ordering': ['-id'],
'get_latest_by': 'id',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]
#wty 迁移操作列表
operations = [
#wty 创建 BlogUser 表的操作
migrations.CreateModel(
name='BlogUser',
fields=[
#wty 主键字段,自增 BigAutoField
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
#wty 密码字段,使用 Django 的密码存储格式
('password', models.CharField(max_length=128, verbose_name='password')),
#wty 最后登录时间字段,记录用户最后一次登录的时间
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
#wty 超级用户标志字段,标记用户是否拥有所有权限
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
#wty 用户名字段,唯一且需要符合用户名验证规则
('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')),
#wty 名字字段,可选
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
#wty 姓氏字段,可选
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
#wty 邮箱字段,可选
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
#wty 员工状态字段,标记用户是否可以访问管理后台
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
#wty 活跃状态字段,标记用户账户是否激活
('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')),
#wty 加入日期字段,记录用户注册时间
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
#wty 自定义昵称字段,用于博客用户系统
('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
#wty 记录创建时间字段,使用当前时间作为默认值
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
#wty 最后修改时间字段,记录用户信息的最后修改时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
#wty 用户来源字段,记录用户的创建来源(如网站注册、第三方登录等)
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
#wty 用户组多对多关系字段,关联到 Django 的 Group 模型
('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')),
#wty 用户权限多对多关系字段,关联到 Django 的 Permission 模型
('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')),
],
#wty 模型元数据配置
options={
'verbose_name': '用户', #wty 单数形式的显示名称
'verbose_name_plural': '用户', #wty 复数形式的显示名称
'ordering': ['-id'], #wty 默认按 ID 倒序排列
'get_latest_by': 'id', #wty 指定获取最新记录的依据字段
},
#wty 指定模型的管理器,使用 Django 默认的 UserManager
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

@ -5,42 +5,55 @@ import django.utils.timezone
class Migration(migrations.Migration):
#wty 声明本迁移的依赖关系,依赖于 accounts 应用的初始迁移
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='bloguser',
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
),
migrations.RemoveField(
model_name='bloguser',
name='created_time',
),
migrations.RemoveField(
model_name='bloguser',
name='last_mod_time',
),
migrations.AddField(
model_name='bloguser',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
migrations.AlterField(
model_name='bloguser',
name='nickname',
field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
),
migrations.AlterField(
model_name='bloguser',
name='source',
field=models.CharField(blank=True, max_length=100, verbose_name='create source'),
),
]
#wty 定义迁移操作序列
operations = [
#wty 修改 BlogUser 模型的元数据选项
migrations.AlterModelOptions(
name='bloguser',
options={
'get_latest_by': 'id', #wty 指定按 id 字段获取最新记录
'ordering': ['-id'], #wty 设置默认按 id 倒序排列
'verbose_name': 'user', #wty 修改单数形式的显示名称为英文
'verbose_name_plural': 'user' #wty 修改复数形式的显示名称为英文
},
),
#wty 删除旧的创建时间字段
migrations.RemoveField(
model_name='bloguser',
name='created_time',
),
#wty 删除旧的最后修改时间字段
migrations.RemoveField(
model_name='bloguser',
name='last_mod_time',
),
#wty 添加新的创建时间字段,使用更规范的字段命名
migrations.AddField(
model_name='bloguser',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
#wty 添加新的最后修改时间字段,使用更规范的字段命名
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
#wty 修改昵称字段的显示名称,从中文改为英文
migrations.AlterField(
model_name='bloguser',
name='nickname',
field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
),
#wty 修改来源字段的显示名称,从中文改为英文
migrations.AlterField(
model_name='bloguser',
name='source',
field=models.CharField(blank=True, max_length=100, verbose_name='create source'),
),
]

@ -1,3 +1,6 @@
#wty 自定义用户模型定义模块
#wty 扩展 Django 默认用户模型,为博客系统添加额外字段和功能
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
@ -6,30 +9,45 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import get_current_site
# Create your models here.
#wty 自定义博客用户模型类,继承自 Django 的 AbstractUser
class BlogUser(AbstractUser):
#wty 昵称字段,用于显示用户的友好名称(非登录用)
nickname = models.CharField(_('nick name'), max_length=100, blank=True)
#wty 创建时间字段,记录用户账户的创建时间,默认值为当前时间
creation_time = models.DateTimeField(_('creation time'), default=now)
#wty 最后修改时间字段,记录用户信息的最后更新时间,默认值为当前时间
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
#wty 用户来源字段,记录用户是通过什么方式注册的(如网站、第三方等)
source = models.CharField(_('create source'), max_length=100, blank=True)
#wty 获取用户详情页的绝对 URL 路径
def get_absolute_url(self):
#wty 使用 reverse 函数生成作者详情页的 URL通过用户名进行路由
return reverse(
'blog:author_detail', kwargs={
'author_name': self.username})
#wty 对象的字符串表示方法
def __str__(self):
#wty 返回邮箱作为对象的字符串表示,便于在管理界面识别
return self.email
#wty 获取用户的完整 URL包含协议和域名
def get_full_url(self):
#wty 获取当前站点的域名
site = get_current_site().domain
#wty 构建完整的 URL包含 HTTPS 协议、域名和路径
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
return url
#wty 模型的元数据配置类
class Meta:
#wty 默认排序规则,按 ID 倒序排列(最新的用户显示在最前面)
ordering = ['-id']
#wty 单数形式的显示名称,用于管理界面
verbose_name = _('user')
#wty 复数形式的显示名称,与单数形式相同
verbose_name_plural = verbose_name
get_latest_by = 'id'
#wty 指定获取最新记录的依据字段
get_latest_by = 'id'

@ -1,28 +1,52 @@
#wty 账户应用 URL 路由配置模块
#wty 定义用户认证相关页面的 URL 路由和视图映射
from django.urls import path
from django.urls import re_path
from . import views
from .forms import LoginForm
#wty 应用命名空间,用于 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'),
]
#wty URL 模式列表,定义所有账户相关的路由规则
urlpatterns = [
#wty 用户登录路由,使用正则表达式匹配 /login/ 路径
re_path(r'^login/$',
#wty 使用类视图 LoginView设置登录成功后的重定向路径
views.LoginView.as_view(success_url='/'),
name='login', #wty 路由名称,用于反向解析
#wty 传递额外参数,指定使用自定义登录表单
kwargs={'authentication_form': LoginForm}),
#wty 用户注册路由,匹配 /register/ 路径
re_path(r'^register/$',
#wty 使用类视图 RegisterView设置注册成功后的重定向路径
views.RegisterView.as_view(success_url="/"),
name='register'), #wty 注册路由名称
#wty 用户登出路由,匹配 /logout/ 路径
re_path(r'^logout/$',
#wty 使用类视图 LogoutView 处理用户登出
views.LogoutView.as_view(),
name='logout'), #wty 登出路由名称
#wty 账户操作结果页面路由,使用 path 函数匹配固定路径
path(r'account/result.html',
#wty 使用函数视图 account_result 处理结果显示
views.account_result,
name='result'), #wty 结果页面路由名称
#wty 忘记密码页面路由,匹配 /forget_password/ 路径
re_path(r'^forget_password/$',
#wty 使用类视图 ForgetPasswordView 处理密码重置请求
views.ForgetPasswordView.as_view(),
name='forget_password'), #wty 忘记密码路由名称
#wty 忘记密码验证码请求路由,匹配 /forget_password_code/ 路径
re_path(r'^forget_password_code/$',
#wty 使用类视图 ForgetPasswordEmailCode 处理验证码发送
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'), #wty 验证码路由名称
]

@ -1,26 +1,42 @@
#wty 自定义认证后端模块
#wty 扩展 Django 认证系统,支持使用邮箱或用户名登录
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
#wty 自定义认证后端类,支持邮箱或用户名登录
class EmailOrUsernameModelBackend(ModelBackend):
"""
允许使用用户名或邮箱登录
#wty 允许使用用户名或邮箱登录的自定义认证后端
#wty 扩展了 Django 的标准 ModelBackend提供更灵活的登录方式
"""
#wty 用户认证方法,验证用户名/邮箱和密码
def authenticate(self, request, username=None, password=None, **kwargs):
#wty 判断输入的是邮箱还是用户名
if '@' in username:
#wty 如果包含@符号,按邮箱处理
kwargs = {'email': username}
else:
#wty 否则按用户名处理
kwargs = {'username': username}
try:
#wty 根据用户名或邮箱查找用户
user = get_user_model().objects.get(**kwargs)
#wty 验证密码是否正确
if user.check_password(password):
#wty 密码正确,返回用户对象
return user
except get_user_model().DoesNotExist:
#wty 用户不存在,返回 None
return None
#wty 根据用户ID获取用户对象的方法
def get_user(self, username):
try:
#wty 通过主键查找用户
return get_user_model().objects.get(pk=username)
except get_user_model().DoesNotExist:
return None
#wty 用户不存在,返回 None
return None

@ -1,3 +1,6 @@
#wty 邮箱验证工具模块
#wty 提供发送验证邮件、验证码管理和验证功能
import typing
from datetime import timedelta
@ -7,9 +10,11 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import send_email
#wty 验证码有效期设置5分钟过期
_code_ttl = timedelta(minutes=5)
#wty 发送验证邮件函数
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
"""发送重设密码验证码
Args:
@ -17,12 +22,15 @@ def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email"))
subject: 邮件主题
code: 验证码
"""
#wty 构建邮件HTML内容包含验证码信息
html_content = _(
"You are resetting the password, the verification code is%(code)s, valid within 5 minutes, please keep it "
"properly") % {'code': code}
#wty 调用邮件发送函数发送验证邮件
send_email([to_mail], subject, html_content)
#wty 验证验证码函数
def verify(email: str, code: str) -> typing.Optional[str]:
"""验证code是否有效
Args:
@ -34,16 +42,23 @@ def verify(email: str, code: str) -> typing.Optional[str]:
这里的错误处理不太合理应该采用raise抛出
否测调用方也需要对error进行处理
"""
#wty 从缓存中获取该邮箱对应的验证码
cache_code = get_code(email)
#wty 比较输入的验证码和缓存中的验证码
if cache_code != code:
#wty 验证码不匹配,返回错误信息
return gettext("Verification code error")
#wty 设置验证码到缓存函数
def set_code(email: str, code: str):
"""设置code"""
#wty 将验证码存入缓存设置过期时间为5分钟
cache.set(email, code, _code_ttl.seconds)
#wty 从缓存获取验证码函数
def get_code(email: str) -> typing.Optional[str]:
"""获取code"""
return cache.get(email)
#wty 从缓存中获取指定邮箱的验证码如果不存在返回None
return cache.get(email)

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