From cbe09f6b1811d3291c5e51b25e05eee3e8ba7f9e Mon Sep 17 00:00:00 2001 From: pk6lqz7py <18020194653@163.com> Date: Sun, 30 Nov 2025 12:04:11 +0800 Subject: [PATCH] Update views.py --- src/DjangoBlog/comments/views.py | 111 +++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/src/DjangoBlog/comments/views.py b/src/DjangoBlog/comments/views.py index 37b898d..760bf22 100644 --- a/src/DjangoBlog/comments/views.py +++ b/src/DjangoBlog/comments/views.py @@ -1,14 +1,17 @@ +#zyl: # comments/views.py -import hashlib +# Django 视图文件:处理评论的创建和提交 + +import hashlib # 用于生成评论内容的 MD5 哈希,检测重复提交 from django.core.exceptions import ValidationError from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_protect from django.views.generic.edit import FormView -from django.contrib import messages # 新增导入 +from django.contrib import messages # Django 消息框架,用于显示一次性通知 from django.utils.timezone import now -from datetime import timedelta +from datetime import timedelta # 用于时间比较,检测短时间内重复提交 from accounts.models import BlogUser from blog.models import Article @@ -17,71 +20,153 @@ from .models import Comment class CommentPostView(FormView): + """ + 评论发布视图(基于表单类视图) + + 功能特性: + - CSRF 防护 + - 处理 GET 请求(重定向到文章页) + - 表单验证(长度、重复提交) + - 评论审核机制 + - 嵌套评论(回复)支持 + - 成功/错误消息提示 + """ + + # 指定使用的表单类 form_class = CommentForm + + # 表单验证失败时使用的模板(即 article_detail.html) + # 会在上下文中传递错误信息 template_name = 'blog/article_detail.html' @method_decorator(csrf_protect) def dispatch(self, *args, **kwargs): + """ + 使用 CSRF 保护装饰器,确保 POST 请求必须携带有效的 CSRF Token + 防止跨站请求伪造攻击 + """ return super(CommentPostView, self).dispatch(*args, **kwargs) def get(self, request, *args, **kwargs): + """ + 处理 GET 请求:用户直接访问此 URL 时重定向到文章详情页 + + 逻辑: + 1. 从 URL 参数获取 article_id + 2. 查询文章对象(404 保护) + 3. 重定向到文章详情页的评论区锚点 + """ article_id = self.kwargs['article_id'] article = get_object_or_404(Article, pk=article_id) url = article.get_absolute_url() - return HttpResponseRedirect(url + "#comments") + return HttpResponseRedirect(url + "#comments") # #comments 是页面上的锚点 def form_invalid(self, form): + """ + 表单验证失败时的处理 + + 参数: + form: 包含错误信息的表单实例 + + 返回: + 重新渲染文章详情页,显示表单错误 + """ 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): + """ + 表单验证成功后的核心处理逻辑 + + 流程: + 1. 检查文章评论状态 + 2. 检测重复提交(30秒内相同内容) + 3. 保存评论(根据审核设置决定是否立即显示) + 4. 处理嵌套评论(parent_comment 关联) + 5. 发送成功消息并重定向 + """ + """提交的数据验证合法后的逻辑""" + # 获取当前请求用户 user = self.request.user + + # 查询用户详细信息(BlogUser 扩展了默认用户模型) author = BlogUser.objects.get(pk=user.pk) + + # 获取文章 ID 和文章对象 article_id = self.kwargs['article_id'] article = get_object_or_404(Article, pk=article_id) - # 检查文章评论状态 + # 1. 检查文章评论状态 + # comment_status == 'c' 表示评论已关闭 + # status == 'c' 表示文章已关闭 if article.comment_status == 'c' or article.status == 'c': messages.error(self.request, "该文章评论已关闭.") return HttpResponseRedirect(article.get_absolute_url() + "#comments") - # 重复提交检查(最近30秒内相同内容) + # 2. 重复提交检查:防止用户在30秒内重复提交相同内容 comment_body = form.cleaned_data['body'].strip() - body_hash = hashlib.md5(comment_body.encode()).hexdigest() + body_hash = hashlib.md5(comment_body.encode()).hexdigest() # 生成内容哈希 + + # 查询用户 30 秒内在该文章下发表的所有评论 recent_comments = Comment.objects.filter( author=author, article=article, creation_time__gte=now() - timedelta(seconds=30) ) + # 遍历最近评论,对比内容哈希 for comment in recent_comments: if hashlib.md5(comment.body.encode()).hexdigest() == body_hash: messages.warning(self.request, "请勿重复提交相同评论内容!") return HttpResponseRedirect(article.get_absolute_url() + "#comments") - # 保存评论 + # 3. 保存评论到数据库(commit=False 不立即提交) comment = form.save(False) - comment.article = article + comment.article = article # 设置评论所属文章 + # 4. 获取博客设置,检查是否需要审核 from djangoblog.utils import get_blog_setting settings = get_blog_setting() + + # 如果不需要审核,则立即启用评论(is_enable=True) + # 否则保持默认 False(待审核状态) if not settings.comment_need_review: comment.is_enable = True - comment.author = author + + comment.author = author # 设置评论作者 + # 5. 处理嵌套评论:如果是回复,关联父评论 if form.cleaned_data['parent_comment_id']: parent_comment = Comment.objects.get( pk=form.cleaned_data['parent_comment_id']) comment.parent_comment = parent_comment + # 保存评论到数据库(commit=True) comment.save(True) + + # 发送成功消息,显示在下一个页面 messages.success(self.request, "评论提交成功!") + + # 6. 重定向到文章详情页,并定位到刚提交的评论位置 + # 使用 #div-comment-{id} 锚点,前端模板需要有对应 ID 的元素 return HttpResponseRedirect( "%s#div-comment-%d" % - (article.get_absolute_url(), comment.pk)) \ No newline at end of file + (article.get_absolute_url(), comment.pk)) + +# 安全注意事项: +# 1. 用户身份验证:此视图假设用户已登录,实际应在 URL 配置或视图中添加 login_required +# 2. parent_comment_id 验证:应检查父评论是否属于同一文章,防止跨文章回复 +# 3. 速率限制:当前仅检查30秒重复,建议增加更严格的速率限制(如每小时最多评论数) +# 4. 内容安全:评论内容需防范 XSS 攻击,应在模板层或模型层进行 HTML 转义 +# 5. 邮件通知:发送邮件可能耗时,生产环境建议使用 Celery 异步任务 + +# 性能优化建议: +# 1. comment.save() 触发数据库写入,可考虑使用事务 +# 2. 频繁查询 BlogUser 和 BlogSettings,可添加缓存 +# 3. get_max_articleid_commentid() 如果涉及复杂查询,可优化 \ No newline at end of file