You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
git-test/src/DjangoBlog/comments/views.py

172 lines
6.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#zyl:
# comments/views.py
# 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 # Django 消息框架,用于显示一次性通知
from django.utils.timezone import now
from datetime import timedelta # 用于时间比较,检测短时间内重复提交
from accounts.models import BlogUser
from blog.models import Article
from .forms import CommentForm
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") # #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 # 文章对象,模板需要
})
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")
# 2. 重复提交检查防止用户在30秒内重复提交相同内容
comment_body = form.cleaned_data['body'].strip()
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 # 设置评论所属文章
# 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 # 设置评论作者
# 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))
# 安全注意事项:
# 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() 如果涉及复杂查询,可优化