diff --git a/doc/2315304412-陆欣颖-软件界面设计说明书.docx b/doc/2315304412-陆欣颖-软件界面设计说明书.docx new file mode 100644 index 0000000..970c291 Binary files /dev/null and b/doc/2315304412-陆欣颖-软件界面设计说明书.docx differ diff --git a/src/django-master/accounts/admin.py b/src/django-master/accounts/admin.py index 32e483c..a702223 100644 --- a/src/django-master/accounts/admin.py +++ b/src/django-master/accounts/admin.py @@ -1,52 +1,52 @@ -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 import forms# 导入Django表单模块,用于创建自定义表单 +from django.contrib.auth.admin import UserAdmin# 导入Django自带的用户管理类,用于继承扩展 +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 +from .models import BlogUser# 导入当前应用下的BlogUser模型(自定义用户模型) -class BlogUserCreationForm(forms.ModelForm): +class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段,使用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',) + model = BlogUser# 关联的模型是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") 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"]) + user = super().save(commit=False)# 先不提交到数据库 + user.set_password(self.cleaned_data["password1"]) # 哈希处理密码 if commit: - user.source = 'adminsite' - user.save() + user.source = 'adminsite'# 标记用户来源为“后台管理” + user.save()# 提交到数据库 return user class BlogUserChangeForm(UserChangeForm): class Meta: - model = BlogUser - fields = '__all__' - field_classes = {'username': UsernameField} + model = BlogUser # 关联的模型是BlogUser + fields = '__all__'# 显示模型的所有字段 + field_classes = {'username': UsernameField}# 为用户名字段指定类(保持Django原生逻辑) def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + super().__init__(*args, **kwargs)# 调用父类的初始化方法 class BlogUserAdmin(UserAdmin): - form = BlogUserChangeForm - add_form = BlogUserCreationForm + form = BlogUserChangeForm# 指定修改用户时使用的表单 + add_form = BlogUserCreationForm# 指定创建用户时使用的表单 list_display = ( 'id', 'nickname', @@ -55,5 +55,5 @@ class BlogUserAdmin(UserAdmin): 'last_login', 'date_joined', 'source') - list_display_links = ('id', 'username') - ordering = ('-id',) + list_display_links = ('id', 'username')# 列表页中可点击跳转的字段 + ordering = ('-id',)# 列表页的排序方式(按ID倒序) diff --git a/src/django-master/accounts/apps.py b/src/django-master/accounts/apps.py index 9b3fc5a..0101a53 100644 --- a/src/django-master/accounts/apps.py +++ b/src/django-master/accounts/apps.py @@ -1,5 +1,5 @@ -from django.apps import AppConfig +from django.apps import AppConfig#导入 Django 框架中用于应用配置的 AppConfig 类,这是 Django 应用配置的核心类 -class AccountsConfig(AppConfig): - name = 'accounts' +class AccountsConfig(AppConfig):#定义 AccountsConfig 类,继承自 AppConfig,用于对 accounts 应用进行自定义配置 + name = 'accounts'#指定该应用的名称为 accounts,Django 会通过这个名称来识别和管理该应用 diff --git a/src/django-master/accounts/forms.py b/src/django-master/accounts/forms.py index fce4137..9ce4052 100644 --- a/src/django-master/accounts/forms.py +++ b/src/django-master/accounts/forms.py @@ -15,7 +15,7 @@ class LoginForm(AuthenticationForm): attrs={'placeholder': "username", "class": "form-control"}) self.fields['password'].widget = widgets.PasswordInput( attrs={'placeholder': "password", "class": "form-control"}) - +#自定义登录表单,在__init__方法中设置username(文本输入,占位符、form-control样式)和password(密码输入,占位符、form-control样式)字段的前端显示样式。 class RegisterForm(UserCreationForm): def __init__(self, *args, **kwargs): @@ -29,17 +29,17 @@ class RegisterForm(UserCreationForm): attrs={'placeholder': "password", "class": "form-control"}) self.fields['password2'].widget = widgets.PasswordInput( attrs={'placeholder': "repeat password", "class": "form-control"}) - +#__init__方法中设置username(文本输入)、email(邮箱输入)、password1和password2(密码输入)字段的占位符与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 - +#clean_email方法验证邮箱是否已被注册,若存在则抛出“邮箱已存在”的验证错误 class Meta: model = get_user_model() fields = ("username", "email") - +#Meta类指定关联模型为自定义用户模型,表单字段包含username和email class ForgetPasswordForm(forms.Form): new_password1 = forms.CharField( @@ -81,7 +81,7 @@ class ForgetPasswordForm(forms.Form): } ), ) - +#定义new_password1(新密码,密码输入)、new_password2(确认密码,密码输入)、email(邮箱,文本输入)、code(验证码,文本输入)字段,均设置form-control样式和占位符。 def clean_new_password2(self): password1 = self.data.get("new_password1") password2 = self.data.get("new_password2") @@ -90,7 +90,7 @@ class ForgetPasswordForm(forms.Form): password_validation.validate_password(password2) return password2 - +# clean_new_password2方法验证两次新密码是否一致,并对密码进行有效性校验 def clean_email(self): user_email = self.cleaned_data.get("email") if not BlogUser.objects.filter( @@ -100,6 +100,7 @@ class ForgetPasswordForm(forms.Form): raise ValidationError(_("email does not exist")) return user_email +# clean_email方法验证邮箱是否已注册(基于BlogUser模型),未注册则抛出“邮箱不存在”的验证错误 def clean_code(self): code = self.cleaned_data.get("code") error = utils.verify( @@ -109,9 +110,10 @@ class ForgetPasswordForm(forms.Form): if error: raise ValidationError(error) return code - +#clean_code方法调用工具方法utils.verify验证验证码有效性,无效则抛出错误 class ForgetPasswordCodeForm(forms.Form): email = forms.EmailField( label=_('Email'), ) +#仅包含email字段(邮箱输入),用于忘记密码流程中验证邮箱的步骤 \ No newline at end of file diff --git a/src/django-master/accounts/models.py b/src/django-master/accounts/models.py index 3baddbb..438173a 100644 --- a/src/django-master/accounts/models.py +++ b/src/django-master/accounts/models.py @@ -8,28 +8,29 @@ 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) +class BlogUser(AbstractUser):#自定义用户模型BlogUser,继承自Django内置的AbstractUser(可扩展的用户抽象类)。 + nickname = models.CharField(_('nick name'), max_length=100, blank=True)#定义nickname字段,字符类型,支持国际化翻译,最大长度100,可为空。 + creation_time = models.DateTimeField(_('creation time'), default=now)#定义creation_time字段,日期时间类型,默认值为当前时间(now方法)。 + last_modify_time = models.DateTimeField(_('last modify time'), default=now)#定义last_modify_time字段,日期时间类型,默认值为当前时间。 + source = models.CharField(_('create source'), max_length=100, blank=True)#定义source字段,字符类型,记录用户创建来源,最大长度100,可为空。 + def get_absolute_url(self): return reverse( 'blog:author_detail', kwargs={ - 'author_name': self.username}) + 'author_name': self.username})#定义获取用户详情页绝对URL的方法,通过reverse反向解析路由blog:author_detail,传递username参数。 def __str__(self): - return self.email + return self.email#定义对象的字符串表示方法,返回用户的email def get_full_url(self): site = get_current_site().domain url = "https://{site}{path}".format(site=site, path=self.get_absolute_url()) - return url + return url#定义获取带域名的完整URL方法,结合当前站点域名和get_absolute_url生成完整链接 class Meta: - ordering = ['-id'] - verbose_name = _('user') - verbose_name_plural = verbose_name - get_latest_by = 'id' + ordering = ['-id']#查询结果按id倒序排列 + verbose_name = _('user')#模型的单数显示名称(支持国际化) + verbose_name_plural = verbose_name#模型的复数显示名称与单数一致。 + get_latest_by = 'id'#指定按id获取最新记录 diff --git a/src/django-master/accounts/tests.py b/src/django-master/accounts/tests.py index 6893411..8cd5036 100644 --- a/src/django-master/accounts/tests.py +++ b/src/django-master/accounts/tests.py @@ -53,7 +53,7 @@ class AccountTest(TestCase): article.save() response = self.client.get(article.get_admin_url()) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 200)#测试管理员账号登录后台功能:创建超级用户,验证登录状态和后台页面访问状态 def test_validate_register(self): self.assertEquals( @@ -116,7 +116,7 @@ class AccountTest(TestCase): self.assertIn(response.status_code, [301, 302, 200]) response = self.client.get(article.get_admin_url()) - self.assertIn(response.status_code, [301, 302, 200]) + self.assertIn(response.status_code, [301, 302, 200])#测试用户注册流程:验证注册前后用户数量变化,邮箱验证链接的有效性,以及注册后用户权限、文章发布等功能 def test_verify_email_code(self): to_email = "admin@admin.com" @@ -128,7 +128,7 @@ class AccountTest(TestCase): self.assertEqual(err, 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( @@ -137,7 +137,7 @@ class AccountTest(TestCase): ) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.content.decode("utf-8"), "ok") + self.assertEqual(resp.content.decode("utf-8"), "ok")#测试忘记密码的邮箱验证码发送:分别验证成功和失败场景(如邮箱错误)的接口响应 def test_forget_password_email_code_fail(self): resp = self.client.post( @@ -203,5 +203,5 @@ class AccountTest(TestCase): data=data ) - self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.status_code, 200)#测试忘记密码流程:成功场景:验证密码修改后是否生效;失败场景:验证不存在用户、验证码错误时的接口响应 diff --git a/src/django-master/accounts/urls.py b/src/django-master/accounts/urls.py index 107a801..4048236 100644 --- a/src/django-master/accounts/urls.py +++ b/src/django-master/accounts/urls.py @@ -9,20 +9,21 @@ app_name = "accounts" urlpatterns = [re_path(r'^login/$', views.LoginView.as_view(success_url='/'), name='login', - kwargs={'authentication_form': LoginForm}), + kwargs={'authentication_form': LoginForm}),#登录路由,对应LoginView,用LoginForm表单,成功重定向/ re_path(r'^register/$', views.RegisterView.as_view(success_url="/"), - name='register'), + name='register'),#注册路由,对应RegisterView,成功重定向/。 + re_path(r'^logout/$', views.LogoutView.as_view(), - name='logout'), + name='logout'),#登出路由,对应LogoutView。 path(r'account/result.html', views.account_result, - name='result'), + name='result'),#结果页路由,对应account_result视图 re_path(r'^forget_password/$', views.ForgetPasswordView.as_view(), - name='forget_password'), + name='forget_password'),#忘记密码路由,对应ForgetPasswordView re_path(r'^forget_password_code/$', views.ForgetPasswordEmailCode.as_view(), - name='forget_password_code'), + name='forget_password_code'),#忘记密码验证码路由,对应ForgetPasswordEmailCode ] diff --git a/src/django-master/accounts/user_login_backend.py b/src/django-master/accounts/user_login_backend.py index 73cdca1..da55cc1 100644 --- a/src/django-master/accounts/user_login_backend.py +++ b/src/django-master/accounts/user_login_backend.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend -class EmailOrUsernameModelBackend(ModelBackend): +class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端,支持用户名或邮箱两种方式登录。 """ 允许使用用户名或邮箱登录 """ @@ -18,9 +18,10 @@ class EmailOrUsernameModelBackend(ModelBackend): return user except get_user_model().DoesNotExist: return None - +#核心认证逻辑:判断输入是否为邮箱(含@),分别用邮箱或用户名查询用户,验证密码后返回用户对象;若用户不存在则返回None。 def get_user(self, username): try: return get_user_model().objects.get(pk=username) except get_user_model().DoesNotExist: return None +#根据用户ID(主键)查询用户,不存在则返回None,用于Django认证系统的用户查询环节 \ No newline at end of file diff --git a/src/django-master/accounts/utils.py b/src/django-master/accounts/utils.py index 4b94bdf..da3a40f 100644 --- a/src/django-master/accounts/utils.py +++ b/src/django-master/accounts/utils.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from djangoblog.utils import send_email -_code_ttl = timedelta(minutes=5) +_code_ttl = timedelta(minutes=5)#验证码有效期,设置为5分钟。 def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")): @@ -44,6 +44,6 @@ def set_code(email: str, code: str): cache.set(email, code, _code_ttl.seconds) -def get_code(email: str) -> typing.Optional[str]: +def get_code(email: str) -> typing.Optional[str]:#从缓存中获取指定邮箱对应的验证码 """获取code""" return cache.get(email) diff --git a/src/django-master/accounts/views.py b/src/django-master/accounts/views.py index ae67aec..2c69a88 100644 --- a/src/django-master/accounts/views.py +++ b/src/django-master/accounts/views.py @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) class RegisterView(FormView): form_class = RegisterForm - template_name = 'account/registration_form.html' + template_name = 'account/registration_form.html'#处理用户注册逻辑,指定表单类RegisterForm和模板account/registration_form.html。 @method_decorator(csrf_protect) def dispatch(self, *args, **kwargs): @@ -77,11 +77,11 @@ class RegisterView(FormView): else: return self.render_to_response({ 'form': form - }) + })#form_valid方法中,保存用户并设置为非活跃状态,生成邮箱验证链接并发送验证邮件,最后重定向到结果页。 class LogoutView(RedirectView): - url = '/login/' + url = '/login/'#处理用户登出,登出后重定向到/login/ @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs): @@ -90,7 +90,7 @@ class LogoutView(RedirectView): def get(self, request, *args, **kwargs): logout(request) delete_sidebar_cache() - return super(LogoutView, self).get(request, *args, **kwargs) + return super(LogoutView, self).get(request, *args, **kwargs)#get方法中调用logout登出用户,删除侧边栏缓存后完成重定向 class LoginView(FormView): @@ -113,8 +113,7 @@ class LoginView(FormView): redirect_to = '/' kwargs['redirect_to'] = redirect_to - return super(LoginView, self).get_context_data(**kwargs) - + return super(LoginView, self).get_context_data(**kwargs)#处理用户登录逻辑,指定表单类LoginForm、模板account / login.html和成功后重定向地址 / def form_valid(self, form): form = AuthenticationForm(data=self.request.POST, request=self.request) @@ -130,7 +129,7 @@ class LoginView(FormView): else: return self.render_to_response({ 'form': form - }) + })#form_valid方法中验证表单,登录用户并根据“记住我”选项设置会话过期时间 def get_success_url(self): @@ -139,7 +138,7 @@ class LoginView(FormView): url=redirect_to, allowed_hosts=[ self.request.get_host()]): redirect_to = self.success_url - return redirect_to + return redirect_to#get_success_url方法处理登录后的重定向地址,确保其安全性 def account_result(request): @@ -147,7 +146,7 @@ def account_result(request): id = request.GET.get('id') user = get_object_or_404(get_user_model(), id=id) - logger.info(type) + logger.info(type)#处理注册和邮箱验证的结果逻辑,根据type参数区分场景: if user.is_active: return HttpResponseRedirect('/') if type and type in ['register', 'validation']: @@ -177,7 +176,7 @@ def account_result(request): class ForgetPasswordView(FormView): form_class = ForgetPasswordForm - template_name = 'account/forget_password.html' + template_name = 'account/forget_password.html'#处理忘记密码逻辑,指定表单类ForgetPasswordForm和模板account/forget_password.html def form_valid(self, form): if form.is_valid(): @@ -186,10 +185,10 @@ class ForgetPasswordView(FormView): blog_user.save() return HttpResponseRedirect('/login/') else: - return self.render_to_response({'form': form}) + return self.render_to_response({'form': form})#form_valid方法中验证表单后,重置用户密码并重定向到登录页 -class ForgetPasswordEmailCode(View): +class ForgetPasswordEmailCode(View):# 处理忘记密码的邮箱验证码发送逻辑 def post(self, request: HttpRequest): form = ForgetPasswordCodeForm(request.POST) @@ -201,4 +200,4 @@ class ForgetPasswordEmailCode(View): utils.send_verify_email(to_email, code) utils.set_code(to_email, code) - return HttpResponse("ok") + return HttpResponse("ok")# post方法中验证邮箱表单,生成并发送验证码,将验证码存入缓存后返回成功标识