From 70e11a4e8d6349e4653276bba259244c82bf06c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E7=91=9E=E8=90=8D?= <1909818175@qq.com> Date: Sat, 22 Nov 2025 21:23:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AE=8C=E5=96=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- comments/admin.py | 19 +++++++++++++++++-- comments/forms.py | 10 +++++++++- comments/models.py | 15 ++++++++++----- comments/urls.py | 3 ++- comments/utils.py | 11 ++++++++++- comments/views.py | 12 ++++++++++++ 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/comments/admin.py b/comments/admin.py index 9f15c1d..f0273bb 100644 --- a/comments/admin.py +++ b/comments/admin.py @@ -6,7 +6,8 @@ from django.contrib import admin from django.urls import reverse # 用于反向生成Admin页面URL from django.utils.html import format_html # 用于安全渲染HTML链接,避免XSS风险 from django.utils.translation import gettext_lazy as _ # 用于后台字段名称国际化翻译 - +from django.contrib.admin.models import LogEntry, ADDITION # 新增导入 +from django.contrib.contenttypes.models import ContentType # 新增导入 # 赵瑞萍:自定义批量操作函数——禁用选中的评论 def disable_commentstatus(modeladmin, request, queryset): @@ -89,4 +90,18 @@ class CommentAdmin(admin.ModelAdmin): # 赵瑞萍:为自定义字段设置后台显示名称,支持国际化翻译,适配多语言环境 link_to_userinfo.short_description = _('User') - link_to_article.short_description = _('Article') \ No newline at end of file + link_to_article.short_description = _('Article') + + def enable_commentstatus(modeladmin, request, queryset): + queryset.update(is_enable=True) + # 记录操作日志 + content_type = ContentType.objects.get_for_model(queryset.model) + for obj in queryset: + LogEntry.objects.create( + user_id=request.user.id, + content_type_id=content_type.id, + object_id=obj.id, + object_repr=str(obj), + action_flag=ADDITION, + change_message="批量启用评论" + ) \ No newline at end of file diff --git a/comments/forms.py b/comments/forms.py index 9a3bb14..51a049e 100644 --- a/comments/forms.py +++ b/comments/forms.py @@ -21,4 +21,12 @@ class CommentForm(ModelForm): # 赵瑞萍:表单元数据配置类,定义模型关联、字段筛选等关键配置 class Meta: model = Comment # 绑定Comment模型,表单数据将直接映射到模型对应的字段 - fields = ['body'] # 仅指定评论正文字段为前端输入项,其他字段(如作者、时间)由后端处理 \ No newline at end of file + fields = ['body'] # 仅指定评论正文字段为前端输入项,其他字段(如作者、时间)由后端处理 + + def clean_body(self): + body = self.cleaned_data.get('body').strip() + if not body: + raise forms.ValidationError("评论内容不能为空") + if len(body) > 300: + raise forms.ValidationError("评论内容不能超过300个字符") + return body \ No newline at end of file diff --git a/comments/models.py b/comments/models.py index 5ff9040..3e7fb25 100644 --- a/comments/models.py +++ b/comments/models.py @@ -7,6 +7,7 @@ from django.conf import settings # 获取项目配置(如用户模型) from django.db import models # 数据库模型基类及字段类型 from django.utils.timezone import now # 获取带时区的当前时间,用于时间字段默认值 from django.utils.translation import gettext_lazy as _ # 国际化翻译工具,实现多语言显示 +from django.utils.html import escape # 新增导入 # 赵瑞萍:导入关联模型,评论需与博客文章建立关联关系 from blog.models import Article # 博客文章模型,评论将关联到具体文章 @@ -21,10 +22,9 @@ class Comment(models.Model): # 赵瑞萍:评论正文字段,限制最大300字符,避免内容过长 body = models.TextField('正文', max_length=300) - # 赵瑞萍:评论创建时间字段,默认值为当前时区时间,支持国际化显示 - creation_time = models.DateTimeField(_('creation time'), default=now) - - # 赵瑞萍:评论最后修改时间字段,默认值为当前时间,可通过代码更新为修改时的时间 + # 赵瑞萍 comments/models.py + # 赵瑞萍 将 creation_time 改为 created_time(与迁移文件保持一致) + created_time = models.DateTimeField(_('creation time'), default=now) last_modify_time = models.DateTimeField(_('last modify time'), default=now) # 赵瑞萍:评论作者外键,关联项目配置的用户模型 @@ -73,4 +73,9 @@ class Comment(models.Model): # 赵瑞萍:定义模型实例的字符串表示形式 # 在后台管理和调试时,直观显示评论内容(取正文作为标识) def __str__(self): - return self.body \ No newline at end of file + return self.body + + def save(self, *args, **kwargs): + # 保存前转义 HTML 特殊字符 + self.body = escape(self.body) + super().save(*args, **kwargs) \ No newline at end of file diff --git a/comments/urls.py b/comments/urls.py index e123114..d79a1d4 100644 --- a/comments/urls.py +++ b/comments/urls.py @@ -20,4 +20,5 @@ urlpatterns = [ views.CommentPostView.as_view(), name='postcomment' # URL名称,用于反向解析(如reverse('comments:postcomment')) ), -] \ No newline at end of file +] + diff --git a/comments/utils.py b/comments/utils.py index fbd27f7..f8eeebd 100644 --- a/comments/utils.py +++ b/comments/utils.py @@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _ # 赵瑞萍:导入项目工具函数,获取站点信息和发送邮件 from djangoblog.utils import get_current_site # 获取当前站点域名,用于构建文章链接 +from django.conf import settings # 新增:导入settings用于获取管理员配置 from djangoblog.utils import send_email # 项目封装的邮件发送函数 # 赵瑞萍:创建当前模块的日志记录器,命名为当前模块名,便于日志定位 @@ -70,4 +71,12 @@ def send_comment_email(comment): send_email([tomail], subject, html_content) except Exception as e: # 赵瑞萍:捕获邮件发送过程中的所有异常,记录错误日志(不中断程序执行) - logger.error(e) \ No newline at end of file + logger.error(e) + # 新增:管理员告警 + if settings.ADMINS: + admin_emails = [admin[1] for admin in settings.ADMINS] + send_email( + admin_emails, + "评论邮件发送失败告警", + f"评论ID: {comment.id} 给被回复者({tomail})的通知邮件发送失败: {str(e)}" + ) \ No newline at end of file diff --git a/comments/views.py b/comments/views.py index 6a48096..ad99cda 100644 --- a/comments/views.py +++ b/comments/views.py @@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError # 数据验证异常,用于抛出评论相关业务错误 from django.http import HttpResponseRedirect # 重定向响应类,用于评论成功后跳转 from django.shortcuts import get_object_or_404 # 安全查询数据,不存在则返回404 +from django.contrib.auth.decorators import login_required # 新增导入 from django.utils.decorators import method_decorator # 为类视图方法添加装饰器 from django.views.decorators.csrf import csrf_protect # CSRF保护装饰器,防止跨站请求伪造 from django.views.generic.edit import FormView # 表单处理通用类视图,简化表单逻辑 @@ -15,6 +16,17 @@ from blog.models import Article # 文章模型,评论需关联具体文章 from .forms import CommentForm # 评论表单,用于前端输入验证 from .models import Comment # 评论模型,用于数据存储 +# 在类定义上方添加装饰器 +@method_decorator(login_required, name='dispatch') # 强制登录验证 +@method_decorator(csrf_protect, name='dispatch') # 原有CSRF保护 +class CommentPostView(FormView): + """ + 赵瑞萍:评论提交处理类视图,继承FormView封装表单处理流程 + 核心功能:接收评论提交请求、验证数据合法性、处理评论保存逻辑、返回对应响应 + 支持场景:普通评论发布、评论回复、评论审核状态控制、CSRF防护 + """ + form_class = CommentForm + template_name = 'blog/article_detail.html' class CommentPostView(FormView): """