/postcomment', # URL路径:包含整数类型的文章ID参数(article_id)
+ # 关联视图类:将CommentPostView类视图转换为可调用的视图函数
views.CommentPostView.as_view(),
- # 为该URL指定名称"postcomment",结合应用命名空间可通过"comments:postcomment"引用
- name='postcomment'
+ name='postcomment' # URL名称,用于反向解析(如reverse('comments:postcomment'))
),
]
\ No newline at end of file
diff --git a/comments/utils.py b/comments/utils.py
index 2becf36..fbd27f7 100644
--- a/comments/utils.py
+++ b/comments/utils.py
@@ -1,34 +1,37 @@
-# 导入日志模块,用于记录程序运行中的信息和错误
+# 赵瑞萍:评论邮件通知模块,用于评论提交后发送邮件通知
+# 核心功能:向评论者发送感谢邮件,向被回复者发送评论回复通知,支持多语言和链接跳转
+
+# 赵瑞萍:导入日志模块,记录邮件发送过程中的信息和异常
import logging
-# 导入Django的翻译函数,用于实现多语言支持,_为常用别名
+# 赵瑞萍:导入Django国际化翻译工具,实现邮件内容多语言支持
from django.utils.translation import gettext_lazy as _
-# 从项目工具模块导入获取当前站点信息和发送邮件的工具函数
-from djangoblog.utils import get_current_site
-from djangoblog.utils import send_email
+# 赵瑞萍:导入项目工具函数,获取站点信息和发送邮件
+from djangoblog.utils import get_current_site # 获取当前站点域名,用于构建文章链接
+from djangoblog.utils import send_email # 项目封装的邮件发送函数
-# 创建当前模块的日志记录器,用于记录该模块相关的日志信息
+# 赵瑞萍:创建当前模块的日志记录器,命名为当前模块名,便于日志定位
logger = logging.getLogger(__name__)
def send_comment_email(comment):
"""
- 发送评论相关的邮件通知
-
- 功能说明:
- 1. 向评论者发送感谢邮件
- 2. 若该评论是回复其他评论的(即有父评论),则向被回复的评论者发送回复通知邮件
+ 赵瑞萍:发送评论相关邮件通知的核心函数
+ 分两种场景发送邮件:
+ 1. 向当前评论的发布者发送感谢评论邮件
+ 2. 若当前评论是回复其他评论(有父评论),向被回复者发送回复通知邮件
+ 参数:comment - 已保存的Comment模型实例,包含评论、作者、关联文章、父评论等信息
"""
- # 获取当前站点的域名(如example.com)
+ # 赵瑞萍:获取当前站点的域名(如www.example.com),用于构建完整的文章访问链接
site = get_current_site().domain
- # 邮件主题:感谢评论(支持多语言)
+ # 赵瑞萍:邮件主题(支持多语言,根据项目语言配置自动切换)
subject = _('Thanks for your comment')
- # 构建评论所属文章的完整URL(包含协议和域名)
+ # 赵瑞萍:构建评论所属文章的完整URL(HTTPS协议+域名+文章绝对路径)
article_url = f"https://{site}{comment.article.get_absolute_url()}"
- # 构建给评论者的邮件内容(HTML格式,支持多语言)
- # 使用占位符替换文章URL和标题
+ # 赵瑞萍:构建给评论者的感谢邮件内容(HTML格式,支持超链接)
+ # 使用字符串格式化替换占位符,注入文章URL和标题
html_content = _("""Thank you very much for your comments on this site
You can visit %(article_title)s
to review your comments,
@@ -36,19 +39,19 @@ def send_comment_email(comment):
If the link above cannot be opened, please copy this link to your browser.
%(article_url)s""") % {
- 'article_url': article_url,
- 'article_title': comment.article.title
+ 'article_url': article_url, # 文章完整访问链接
+ 'article_title': comment.article.title # 评论所属文章的标题
}
- # 获取评论者的邮箱地址
+ # 赵瑞萍:获取当前评论者的邮箱地址(从评论作者关联的用户模型中获取)
tomail = comment.author.email
- # 发送感谢邮件给评论者
+ # 赵瑞萍:调用邮件发送函数,向评论者发送感谢邮件
send_email([tomail], subject, html_content)
try:
- # 检查当前评论是否有父评论(即是否是回复其他评论)
+ # 赵瑞萍:判断当前评论是否有父评论(即是否是对其他评论的回复)
if comment.parent_comment:
- # 构建给被回复者的邮件内容(HTML格式,支持多语言)
+ # 赵瑞萍:构建给被回复者的邮件内容(HTML格式,告知其评论收到回复)
html_content = _("""Your comment on %(article_title)s
has
received a reply.
%(comment_body)s
@@ -57,14 +60,14 @@ def send_comment_email(comment):
If the link above cannot be opened, please copy this link to your browser.
%(article_url)s
""") % {
- 'article_url': article_url,
- 'article_title': comment.article.title,
- 'comment_body': comment.parent_comment.body # 被回复的评论内容
+ 'article_url': article_url, # 文章完整访问链接
+ 'article_title': comment.article.title, # 文章标题
+ 'comment_body': comment.parent_comment.body # 被回复的原评论内容
}
- # 获取被回复评论者的邮箱地址
+ # 赵瑞萍:获取被回复评论者的邮箱地址(父评论的作者邮箱)
tomail = comment.parent_comment.author.email
- # 发送回复通知邮件给被回复者
+ # 赵瑞萍:发送回复通知邮件给被回复者
send_email([tomail], subject, html_content)
except Exception as e:
- # 若发送过程中出现异常,记录错误日志
+ # 赵瑞萍:捕获邮件发送过程中的所有异常,记录错误日志(不中断程序执行)
logger.error(e)
\ No newline at end of file
diff --git a/comments/views.py b/comments/views.py
index 9b99bb0..6a48096 100644
--- a/comments/views.py
+++ b/comments/views.py
@@ -1,96 +1,115 @@
-# Create your views here.
-# 导入Django相关异常、响应和工具类
-from django.core.exceptions import ValidationError # 用于抛出数据验证异常
-from django.http import HttpResponseRedirect # 用于重定向HTTP响应
-from django.shortcuts import get_object_or_404 # 根据主键获取对象,不存在则返回404
-from django.utils.decorators import method_decorator # 用于为类视图方法添加装饰器
-from django.views.decorators.csrf import csrf_protect # CSRF保护装饰器
-from django.views.generic.edit import FormView # 表单处理的通用类视图
-
-# 导入相关模型和表单
-from accounts.models import BlogUser # 用户模型
-from blog.models import Article # 文章模型
-from .forms import CommentForm # 评论表单
-from .models import Comment # 评论模型
+# 赵瑞萍:评论提交视图模块,用于处理用户评论发布、数据验证、业务逻辑处理及响应返回
+# 基于Django FormView实现,支持CSRF保护、表单验证、评论状态控制及回复功能处理
+
+# 赵瑞萍:导入Django核心组件,提供异常处理、响应、数据查询及装饰器支持
+from django.core.exceptions import ValidationError # 数据验证异常,用于抛出评论相关业务错误
+from django.http import HttpResponseRedirect # 重定向响应类,用于评论成功后跳转
+from django.shortcuts import get_object_or_404 # 安全查询数据,不存在则返回404
+from django.utils.decorators import method_decorator # 为类视图方法添加装饰器
+from django.views.decorators.csrf import csrf_protect # CSRF保护装饰器,防止跨站请求伪造
+from django.views.generic.edit import FormView # 表单处理通用类视图,简化表单逻辑
+
+# 赵瑞萍:导入关联模型和表单,支撑评论业务数据处理
+from accounts.models import BlogUser # 自定义用户模型,用于关联评论作者
+from blog.models import Article # 文章模型,评论需关联具体文章
+from .forms import CommentForm # 评论表单,用于前端输入验证
+from .models import Comment # 评论模型,用于数据存储
class CommentPostView(FormView):
- """评论提交处理的类视图"""
- form_class = CommentForm # 指定使用的表单类
- template_name = 'blog/article_detail.html' # 表单渲染和错误显示的模板
+ """
+ 赵瑞萍:评论提交处理类视图,继承FormView封装表单处理流程
+ 核心功能:接收评论提交请求、验证数据合法性、处理评论保存逻辑、返回对应响应
+ 支持场景:普通评论发布、评论回复、评论审核状态控制、CSRF防护
+ """
+ # 赵瑞萍:指定表单类为CommentForm,用于数据验证和字段映射
+ form_class = CommentForm
+ # 赵瑞萍:指定模板为文章详情页,用于表单错误时重新渲染页面并显示错误
+ template_name = 'blog/article_detail.html'
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
"""
- 重写dispatch方法,添加CSRF保护
-
- 使用method_decorator将csrf_protect装饰器应用到dispatch方法,
- 确保表单提交经过CSRF验证,防止跨站请求伪造攻击
+ 赵瑞萍:重写dispatch方法,添加CSRF保护
+ 通过method_decorator将csrf_protect装饰器应用到请求分发流程,确保所有请求经过CSRF验证
+ 防止跨站请求伪造攻击,保护评论提交接口安全
"""
+ # 赵瑞萍:调用父类dispatch方法,维持原有请求分发逻辑
return super(CommentPostView, self).dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
"""
- 处理GET请求
-
- 当通过GET访问评论提交URL时,重定向到对应的文章详情页的评论区
+ 赵瑞萍:处理GET请求,避免直接通过URL访问评论提交接口
+ 当用户以GET方式访问时,重定向到对应文章详情页的评论区
"""
- article_id = self.kwargs['article_id'] # 从URL参数中获取文章ID
- article = get_object_or_404(Article, pk=article_id) # 获取对应的文章对象
- url = article.get_absolute_url() # 获取文章的绝对URL
- return HttpResponseRedirect(url + "#comments") # 重定向到文章详情页的评论锚点
+ # 赵瑞萍:从URL参数中获取文章ID,确定评论所属文章
+ article_id = self.kwargs['article_id']
+ # 赵瑞萍:查询文章对象,不存在则返回404
+ article = get_object_or_404(Article, pk=article_id)
+ # 赵瑞萍:获取文章绝对URL,拼接评论区锚点,重定向到评论区
+ url = article.get_absolute_url()
+ return HttpResponseRedirect(url + "#comments")
def form_invalid(self, form):
"""
- 表单数据验证失败时的处理
-
- 当表单提交的数据验证不通过(如必填项为空、格式错误等),
- 重新渲染文章详情页,并携带错误表单对象和文章对象,方便前端显示错误信息
+ 赵瑞萍:表单数据验证失败时的处理方法
+ 当用户输入不符合规则(如评论为空、长度超限),重新渲染文章详情页并携带错误信息
"""
- article_id = self.kwargs['article_id'] # 获取文章ID
- article = get_object_or_404(Article, pk=article_id) # 获取文章对象
+ # 赵瑞萍:从URL参数获取文章ID,关联当前评论的文章
+ article_id = self.kwargs['article_id']
+ # 赵瑞萍:查询文章对象,确保页面渲染时有文章数据
+ article = get_object_or_404(Article, pk=article_id)
+ # 赵瑞萍:返回渲染后的页面,携带包含错误信息的表单和文章对象
return self.render_to_response({
- 'form': form, # 包含错误信息的表单对象
- 'article': article # 文章对象,用于页面渲染
+ 'form': form, # 含错误提示的表单对象,前端可渲染错误信息
+ 'article': article # 文章对象,用于页面展示文章内容
})
def form_valid(self, form):
"""
- 表单数据验证合法后的处理逻辑
-
- 当表单数据验证通过后,执行评论保存等业务逻辑
+ 赵瑞萍:表单数据验证通过后的核心业务逻辑处理
+ 完成评论对象的构建、关联数据设置、业务规则校验及保存,最后重定向到评论位置
"""
- user = self.request.user # 获取当前登录用户
- author = BlogUser.objects.get(pk=user.pk) # 根据用户ID获取对应的用户对象
- article_id = self.kwargs['article_id'] # 获取文章ID
- article = get_object_or_404(Article, pk=article_id) # 获取文章对象
-
- # 检查文章评论状态:若文章评论关闭或文章状态为关闭,则抛出验证异常
+ # 赵瑞萍:获取当前登录用户对象(需登录才能评论)
+ user = self.request.user
+ # 赵瑞萍:根据用户ID查询BlogUser实例,作为评论作者
+ author = BlogUser.objects.get(pk=user.pk)
+ # 赵瑞萍:从URL参数获取文章ID,确定评论所属文章
+ article_id = self.kwargs['article_id']
+ # 赵瑞萍:查询文章对象,不存在则返回404
+ article = get_object_or_404(Article, pk=article_id)
+
+ # 赵瑞萍:业务规则校验——检查文章是否允许评论
+ # 若文章评论状态为'c'(关闭)或文章状态为'c'(关闭),抛出验证异常
if article.comment_status == 'c' or article.status == 'c':
raise ValidationError("该文章评论已关闭.")
- # 保存表单数据但不提交到数据库(commit=False),以便后续补充字段
+ # 赵瑞萍:保存表单数据但不提交到数据库(commit=False),预留字段补充空间
comment = form.save(False)
- comment.article = article # 关联评论对应的文章
+ comment.article = article # 关联评论到对应的文章
- # 获取博客设置,判断评论是否需要审核
+ # 赵瑞萍:获取博客系统设置,判断评论是否需要审核
from djangoblog.utils import get_blog_setting
settings = get_blog_setting()
+ # 赵瑞萍:若无需审核,直接设置评论为启用状态(可立即显示)
if not settings.comment_need_review:
- comment.is_enable = True # 若无需审核,直接设置评论为启用状态
+ comment.is_enable = True
- comment.author = author # 设置评论的作者
+ comment.author = author # 关联评论到作者
- # 处理回复功能:若存在父评论ID,则关联父评论
+ # 赵瑞萍:处理评论回复功能——判断是否存在父评论ID
if form.cleaned_data['parent_comment_id']:
+ # 赵瑞萍:根据父评论ID查询父评论对象,建立回复关联
parent_comment = Comment.objects.get(
pk=form.cleaned_data['parent_comment_id'])
- comment.parent_comment = parent_comment
+ comment.parent_comment = parent_comment # 关联当前评论到父评论
- comment.save(True) # 最终保存评论到数据库
+ # 赵瑞萍:最终将评论数据提交到数据库保存
+ comment.save(True)
- # 重定向到文章详情页中当前评论的锚点位置
+ # 赵瑞萍:重定向到文章详情页中当前评论的锚点位置,方便用户查看自己的评论
return HttpResponseRedirect(
"%s#div-comment-%d" %
- (article.get_absolute_url(), comment.pk))
\ No newline at end of file
+ (article.get_absolute_url(), comment.pk) # 拼接文章URL和评论锚点(如#div-comment-1)
+ )
\ No newline at end of file