diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/djangoBlogStudy.iml b/.idea/djangoBlogStudy.iml new file mode 100644 index 0000000..8b8c395 --- /dev/null +++ b/.idea/djangoBlogStudy.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..db8786c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..eb9a5a1 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/accounts/admin.py b/src/accounts/admin.py index 32e483c..271de0a 100644 --- a/src/accounts/admin.py +++ b/src/accounts/admin.py @@ -7,33 +7,34 @@ from django.utils.translation import gettext_lazy as _ # Register your models here. from .models import BlogUser - +# 用户创建表单 - 处理新用户创建时的密码验证和保存逻辑 class BlogUserCreationForm(forms.ModelForm): - password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput) - password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput) + 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): + def clean_password2(self):# 密码验证方法 - 检查两次输入的密码是否一致 # Check that the two password entries match password1 = self.cleaned_data.get("password1") - password2 = self.cleaned_data.get("password2") + password2 = self.cleaned_data.get("password2")# 获取两次输入的密码值 if password1 and password2 and password1 != password2: - raise forms.ValidationError(_("passwords do not match")) + raise forms.ValidationError(_("passwords do not match"))# 密码一致性检查 - 如果两次输入不一致则抛出验证错误 return password2 - def save(self, commit=True): + def save(self, commit=True):# 用户保存方法 - 处理密码哈希化和用户来源记录 # Save the provided password in hashed format user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) + # 提交保存 - 设置用户来源为管理后台并保存到数据库 if commit: user.source = 'adminsite' user.save() return user - +# 用户修改表单 - 继承Django默认用户修改表单 class BlogUserChangeForm(UserChangeForm): class Meta: model = BlogUser @@ -43,7 +44,7 @@ class BlogUserChangeForm(UserChangeForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - +# 用户管理类 - 自定义用户模型在Django后台的显示和管理方式 class BlogUserAdmin(UserAdmin): form = BlogUserChangeForm add_form = BlogUserCreationForm diff --git a/src/accounts/apps.py b/src/accounts/apps.py index 9b3fc5a..e9be36f 100644 --- a/src/accounts/apps.py +++ b/src/accounts/apps.py @@ -1,5 +1,5 @@ from django.apps import AppConfig - +# 账户应用配置类 class AccountsConfig(AppConfig): name = 'accounts' diff --git a/src/accounts/forms.py b/src/accounts/forms.py index fce4137..e62f722 100644 --- a/src/accounts/forms.py +++ b/src/accounts/forms.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from . import utils from .models import BlogUser - +# 用户登录表单 - 处理用户登录认证 class LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) @@ -16,7 +16,7 @@ class LoginForm(AuthenticationForm): self.fields['password'].widget = widgets.PasswordInput( attrs={'placeholder': "password", "class": "form-control"}) - +# 用户注册表单 - 处理新用户注册流程 class RegisterForm(UserCreationForm): def __init__(self, *args, **kwargs): super(RegisterForm, self).__init__(*args, **kwargs) @@ -40,8 +40,9 @@ class RegisterForm(UserCreationForm): model = get_user_model() fields = ("username", "email") - +# 密码重置表单 - 处理用户忘记密码时的重置流程 class ForgetPasswordForm(forms.Form): + # 新密码字段 - 用户设置的新密码 new_password1 = forms.CharField( label=_("New password"), widget=forms.PasswordInput( @@ -51,7 +52,7 @@ class ForgetPasswordForm(forms.Form): } ), ) - + # 确认密码字段 - 再次输入新密码用于验证 new_password2 = forms.CharField( label="确认密码", widget=forms.PasswordInput( @@ -61,7 +62,7 @@ class ForgetPasswordForm(forms.Form): } ), ) - + # 邮箱字段 - 用户注册时使用的邮箱地址 email = forms.EmailField( label='邮箱', widget=forms.TextInput( @@ -71,7 +72,7 @@ class ForgetPasswordForm(forms.Form): } ), ) - + # 验证码字段 - 邮箱接收的验证码 code = forms.CharField( label=_('Code'), widget=forms.TextInput( @@ -82,6 +83,7 @@ class ForgetPasswordForm(forms.Form): ), ) + # 密码确认验证 - 检查两次输入的密码是否一致 def clean_new_password2(self): password1 = self.data.get("new_password1") password2 = self.data.get("new_password2") @@ -91,6 +93,7 @@ class ForgetPasswordForm(forms.Form): return password2 + # 邮箱验证 - 检查邮箱是否在系统中注册 def clean_email(self): user_email = self.cleaned_data.get("email") if not BlogUser.objects.filter( @@ -100,6 +103,7 @@ class ForgetPasswordForm(forms.Form): raise ValidationError(_("email does not exist")) return user_email + # 验证码验证 - 检查邮箱验证码是否正确 def clean_code(self): code = self.cleaned_data.get("code") error = utils.verify( @@ -110,7 +114,7 @@ class ForgetPasswordForm(forms.Form): raise ValidationError(error) return code - +# 验证码请求表单 - 用于请求发送密码重置验证码 class ForgetPasswordCodeForm(forms.Form): email = forms.EmailField( label=_('Email'), diff --git a/src/accounts/migrations/0001_initial.py b/src/accounts/migrations/0001_initial.py index d2fbcab..2c58aeb 100644 --- a/src/accounts/migrations/0001_initial.py +++ b/src/accounts/migrations/0001_initial.py @@ -18,28 +18,28 @@ class Migration(migrations.Migration): 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')), + ('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')), + ('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')), + ('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'], + 'ordering': ['-id'],#按ID降序排列 'get_latest_by': 'id', }, managers=[ diff --git a/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py index 1a9f509..e2881c8 100644 --- a/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py +++ b/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py @@ -11,33 +11,40 @@ class Migration(migrations.Migration): ] 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', diff --git a/src/accounts/models.py b/src/accounts/models.py index 3baddbb..049a10b 100644 --- a/src/accounts/models.py +++ b/src/accounts/models.py @@ -7,21 +7,24 @@ from djangoblog.utils import get_current_site # Create your models here. - +# 博客用户模型类 - 继承Django抽象用户基类 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) + # URL方法 - 获取用户详情页的相对URL路径 def get_absolute_url(self): return reverse( 'blog:author_detail', kwargs={ 'author_name': self.username}) + # 字符串表示 - 定义对象的字符串显示格式 def __str__(self): return self.email + # 完整URL方法 - 生成包含域名的用户完整详情页URL def get_full_url(self): site = get_current_site().domain url = "https://{site}{path}".format(site=site, diff --git a/src/accounts/tests.py b/src/accounts/tests.py index 6893411..eefe446 100644 --- a/src/accounts/tests.py +++ b/src/accounts/tests.py @@ -10,7 +10,7 @@ from . import utils # Create your tests here. - +# 账户功能测试类 - 继承Django测试基类 class AccountTest(TestCase): def setUp(self): self.client = Client() @@ -22,6 +22,7 @@ class AccountTest(TestCase): ) self.new_test = "xxx123--=" + # 账户验证测试 - 测试超级用户登录和管理权限 def test_validate_account(self): site = get_current_site().domain user = BlogUser.objects.create_superuser( @@ -36,13 +37,13 @@ class AccountTest(TestCase): 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" @@ -55,6 +56,7 @@ class AccountTest(TestCase): response = self.client.get(article.get_admin_url()) self.assertEqual(response.status_code, 200) + # 注册流程测试 - 测试用户完整注册流程 def test_validate_register(self): self.assertEquals( 0, len( @@ -118,6 +120,7 @@ class AccountTest(TestCase): 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() @@ -130,6 +133,7 @@ class AccountTest(TestCase): err = utils.verify("admin@123.com", code) self.assertEqual(type(err), str) + # 密码重置邮件发送成功测试 def test_forget_password_email_code_success(self): resp = self.client.post( path=reverse("account:forget_password_code"), @@ -139,6 +143,7 @@ class AccountTest(TestCase): self.assertEqual(resp.status_code, 200) 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"), @@ -152,6 +157,7 @@ class AccountTest(TestCase): ) 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) @@ -174,6 +180,7 @@ class AccountTest(TestCase): self.assertNotEqual(blog_user, None) 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, @@ -188,7 +195,7 @@ class AccountTest(TestCase): 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) diff --git a/src/accounts/urls.py b/src/accounts/urls.py index 107a801..4768eee 100644 --- a/src/accounts/urls.py +++ b/src/accounts/urls.py @@ -5,24 +5,24 @@ from . import views from .forms import LoginForm app_name = "accounts" - -urlpatterns = [re_path(r'^login/$', +# URL路由配置 - 定义URL路径与视图函数的映射关系 +urlpatterns = [re_path(r'^login/$', # 用户登录路由 - 处理用户登录认证 views.LoginView.as_view(success_url='/'), name='login', kwargs={'authentication_form': LoginForm}), - re_path(r'^register/$', + re_path(r'^register/$', # 用户注册路由 - 处理新用户注册 views.RegisterView.as_view(success_url="/"), name='register'), - re_path(r'^logout/$', + re_path(r'^logout/$',# 用户退出路由 - 处理用户登出操作 views.LogoutView.as_view(), name='logout'), - path(r'account/result.html', + path(r'account/result.html',# 账户结果页面路由 - 显示操作结果信息(如验证结果) views.account_result, name='result'), - re_path(r'^forget_password/$', + re_path(r'^forget_password/$',# 密码重置路由 - 处理忘记密码重置请求 views.ForgetPasswordView.as_view(), name='forget_password'), - re_path(r'^forget_password_code/$', + re_path(r'^forget_password_code/$',# 密码重置验证码路由 - 处理密码重置验证码发送 views.ForgetPasswordEmailCode.as_view(), name='forget_password_code'), ] diff --git a/src/accounts/user_login_backend.py b/src/accounts/user_login_backend.py index 73cdca1..a039074 100644 --- a/src/accounts/user_login_backend.py +++ b/src/accounts/user_login_backend.py @@ -2,11 +2,13 @@ from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend +# 用户认证方法 - 验证用户凭据并返回用户对象 class EmailOrUsernameModelBackend(ModelBackend): """ 允许使用用户名或邮箱登录 """ + # 用户认证方法 - 验证用户凭据并返回用户对象 def authenticate(self, request, username=None, password=None, **kwargs): if '@' in username: kwargs = {'email': username} @@ -19,6 +21,7 @@ class EmailOrUsernameModelBackend(ModelBackend): except get_user_model().DoesNotExist: return None + # 用户获取方法 - 根据用户ID获取用户对象 def get_user(self, username): try: return get_user_model().objects.get(pk=username) diff --git a/src/accounts/utils.py b/src/accounts/utils.py index 4b94bdf..14665a7 100644 --- a/src/accounts/utils.py +++ b/src/accounts/utils.py @@ -9,7 +9,7 @@ from djangoblog.utils import send_email _code_ttl = timedelta(minutes=5) - +# 验证邮件发送函数 - 向指定邮箱发送验证码邮件 def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")): """发送重设密码验证码 Args: @@ -22,7 +22,7 @@ def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")) "properly") % {'code': code} send_email([to_mail], subject, html_content) - +# 验证码验证函数 - 检查用户输入的验证码是否正确 def verify(email: str, code: str) -> typing.Optional[str]: """验证code是否有效 Args: @@ -38,12 +38,12 @@ def verify(email: str, code: str) -> typing.Optional[str]: if cache_code != code: return gettext("Verification code error") - +# 验证码存储函数 - 将验证码保存到缓存系统 def set_code(email: str, code: str): """设置code""" cache.set(email, code, _code_ttl.seconds) - +# 验证码获取函数 - 从缓存系统中获取验证码 def get_code(email: str) -> typing.Optional[str]: """获取code""" return cache.get(email) diff --git a/src/accounts/views.py b/src/accounts/views.py index ae67aec..a40d4ae 100644 --- a/src/accounts/views.py +++ b/src/accounts/views.py @@ -30,30 +30,33 @@ logger = logging.getLogger(__name__) # Create your views here. - +# 用户注册视图 - 处理新用户注册流程 class RegisterView(FormView): form_class = RegisterForm template_name = 'account/registration_form.html' + # 请求分发 - 添加CSRF保护装饰器 @method_decorator(csrf_protect) def dispatch(self, *args, **kwargs): return super(RegisterView, self).dispatch(*args, **kwargs) + # 表单验证成功处理 - 保存用户并发送验证邮件 def form_valid(self, form): 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' path = reverse('account:result') url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format( site=site, path=path, id=user.id, sign=sign) - + # 邮件内容构建 - 创建验证邮件的HTML内容 content = """

请点击下面链接验证您的邮箱

@@ -64,6 +67,7 @@ class RegisterView(FormView): 如果上面链接无法打开,请将此链接复制至浏览器。 {url} """.format(url=url) + # 邮件发送 - 发送验证邮件到用户邮箱 send_email( emailto=[ user.email, @@ -79,7 +83,7 @@ class RegisterView(FormView): 'form': form }) - +# 用户退出视图 - 处理用户登出操作 class LogoutView(RedirectView): url = '/login/' @@ -92,7 +96,7 @@ class LogoutView(RedirectView): delete_sidebar_cache() return super(LogoutView, self).get(request, *args, **kwargs) - +# 用户登录视图 - 处理用户登录认证 class LoginView(FormView): form_class = LoginForm template_name = 'account/login.html' @@ -107,6 +111,7 @@ class LoginView(FormView): return super(LoginView, self).dispatch(request, *args, **kwargs) + # 上下文数据 - 获取重定向目标并添加到上下文 def get_context_data(self, **kwargs): redirect_to = self.request.GET.get(self.redirect_field_name) if redirect_to is None: @@ -115,6 +120,7 @@ class LoginView(FormView): return super(LoginView, self).get_context_data(**kwargs) + # 表单验证成功处理 - 执行用户登录操作 def form_valid(self, form): form = AuthenticationForm(data=self.request.POST, request=self.request) @@ -132,6 +138,7 @@ class LoginView(FormView): 'form': form }) + # 成功URL获取 - 处理登录后的重定向逻辑 def get_success_url(self): redirect_to = self.request.POST.get(self.redirect_field_name) @@ -141,7 +148,7 @@ class LoginView(FormView): redirect_to = self.success_url return redirect_to - +# 账户结果页面视图 - 显示注册或验证结果 def account_result(request): type = request.GET.get('type') id = request.GET.get('id') @@ -174,7 +181,7 @@ def account_result(request): else: return HttpResponseRedirect('/') - +# 密码重置视图 - 处理用户忘记密码重置 class ForgetPasswordView(FormView): form_class = ForgetPasswordForm template_name = 'account/forget_password.html' @@ -188,7 +195,7 @@ class ForgetPasswordView(FormView): else: return self.render_to_response({'form': form}) - +# 密码重置验证码发送视图 - 处理验证码邮件发送 class ForgetPasswordEmailCode(View): def post(self, request: HttpRequest):