diff --git a/src/DjangoBlog/comments/admin.py b/src/DjangoBlog/comments/admin.py
index 1e9bc73..9a0e75a 100644
--- a/src/DjangoBlog/comments/admin.py
+++ b/src/DjangoBlog/comments/admin.py
@@ -1,49 +1,81 @@
+# zy: 评论管理模块 - 配置Django后台管理中评论模型的显示和操作功能
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
+# zy: 禁用评论状态 - 管理员动作函数,用于批量禁用评论
def disable_commentstatus(queryset):
+ # zy: 将选中的评论集的is_enable字段更新为False(禁用)
queryset.update(is_enable=False)
+# zy: 启用评论状态 - 管理员动作函数,用于批量启用评论
def enable_commentstatus(queryset):
+ # zy: 将选中的评论集的is_enable字段更新为True(启用)
queryset.update(is_enable=True)
+# zy: 设置动作在后台显示的描述文字(支持国际化)
disable_commentstatus.short_description = _('Disable comments')
enable_commentstatus.short_description = _('Enable comments')
+# zy: 评论管理类 - 自定义Django后台的评论管理界面
class CommentAdmin(admin.ModelAdmin):
+ # zy: 设置列表页每页显示20条评论
list_per_page = 20
+
+ # zy: 定义列表页显示的字段列
list_display = (
- 'id',
- 'body',
- 'link_to_userinfo',
- 'link_to_article',
- 'is_enable',
- 'creation_time')
+ 'id', # zy: 评论ID
+ 'body', # zy: 评论内容
+ 'link_to_userinfo', # zy: 自定义方法 - 用户信息链接
+ 'link_to_article', # zy: 自定义方法 - 文章链接
+ 'is_enable', # zy: 是否启用状态
+ 'creation_time' # zy: 创建时间
+ )
+
+ # zy: 设置哪些字段可以作为链接点击进入编辑页
list_display_links = ('id', 'body', 'is_enable')
- list_filter = ('is_enable',)
+
+ # zy: 设置右侧过滤侧边栏的过滤条件
+ list_filter = ('is_enable',) # zy: 按启用状态过滤
+
+ # zy: 排除在编辑表单中显示的字段(系统自动管理的字段)
exclude = ('creation_time', 'last_modify_time')
+
+ # zy: 定义批量操作动作列表
actions = [disable_commentstatus, enable_commentstatus]
+
+ # zy: 设置使用弹出窗口选择关联对象的字段(优化性能)
raw_id_fields = ('author', 'article')
- search_fields = ('body',)
+ # zy: 设置可搜索的字段
+ search_fields = ('body',) # zy: 按评论内容搜索
+
+ # zy: 自定义方法 - 生成用户信息链接
def link_to_userinfo(self, obj):
+ # zy: 获取作者模型的app名称和模型名称
info = (obj.author._meta.app_label, obj.author._meta.model_name)
+ # zy: 生成作者编辑页面的URL
link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
+ # zy: 返回HTML链接,显示用户昵称或邮箱
return format_html(
u'%s' %
(link, obj.author.nickname if obj.author.nickname else obj.author.email))
+ # zy: 自定义方法 - 生成文章链接
def link_to_article(self, obj):
+ # zy: 获取文章模型的app名称和模型名称
info = (obj.article._meta.app_label, obj.article._meta.model_name)
+ # zy: 生成文章编辑页面的URL
link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,))
+ # zy: 返回HTML链接,显示文章标题
return format_html(
u'%s' % (link, obj.article.title))
+ # zy: 设置自定义方法在列表页显示的列标题(支持国际化)
link_to_userinfo.short_description = _('User')
- link_to_article.short_description = _('Article')
+ link_to_article.short_description = _('Article')
\ No newline at end of file
diff --git a/src/DjangoBlog/comments/apps.py b/src/DjangoBlog/comments/apps.py
index ff01b77..a6fbd56 100644
--- a/src/DjangoBlog/comments/apps.py
+++ b/src/DjangoBlog/comments/apps.py
@@ -1,5 +1,8 @@
+# zy: 评论应用配置模块 - 定义comments应用的配置信息
from django.apps import AppConfig
-
+# zy: 评论应用配置类 - 继承自Django的AppConfig基类
class CommentsConfig(AppConfig):
+ # zy: 应用名称 - 指定Django内部使用的应用标识
+ # 这个名称应该与INSTALLED_APPS中的名称一致
name = 'comments'
diff --git a/src/DjangoBlog/comments/forms.py b/src/DjangoBlog/comments/forms.py
index e83737d..8f04b50 100644
--- a/src/DjangoBlog/comments/forms.py
+++ b/src/DjangoBlog/comments/forms.py
@@ -1,13 +1,17 @@
+# zy: 评论表单模块 - 定义评论相关的表单类和验证逻辑
from django import forms
from django.forms import ModelForm
-
+# zy: 导入评论模型,用于创建基于模型的自定义表单
from .models import Comment
-
+# zy: 评论表单类 - 继承自ModelForm,用于处理评论的创建和验证
class CommentForm(ModelForm):
+ # zy: 父评论ID字段 - 用于实现评论回复功能
+ # 这是一个隐藏字段,不在页面显示,用于记录回复的父评论
parent_comment_id = forms.IntegerField(
widget=forms.HiddenInput, required=False)
+ # zy: 表单元数据配置类 - 定义表单与模型的关联关系
class Meta:
model = Comment
fields = ['body']
diff --git a/src/DjangoBlog/comments/models.py b/src/DjangoBlog/comments/models.py
index 7c3bbc8..12e7509 100644
--- a/src/DjangoBlog/comments/models.py
+++ b/src/DjangoBlog/comments/models.py
@@ -1,38 +1,44 @@
+# zy: 评论数据模型模块 - 定义评论的数据结构和数据库表结构
from django.conf import settings
from django.db import models
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
-
+# zy: 导入博客文章模型,用于建立评论与文章的关联
from blog.models import Article
-# Create your models here.
+# zy: 评论数据模型类 - 继承自Django的Model基类,对应数据库中的评论表
class Comment(models.Model):
body = models.TextField('正文', max_length=300)
creation_time = models.DateTimeField(_('creation time'), default=now)
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
+ # zy: 作者外键字段 - 关联到用户模型,删除用户时级联删除其评论
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_('author'),
on_delete=models.CASCADE)
+ # zy: 文章外键字段 - 关联到博客文章模型,删除文章时级联删除相关评论
article = models.ForeignKey(
Article,
verbose_name=_('article'),
on_delete=models.CASCADE)
+ # zy: 父评论自关联字段 - 实现评论回复功能,允许评论有父评论
parent_comment = models.ForeignKey(
'self',
verbose_name=_('parent comment'),
blank=True,
null=True,
on_delete=models.CASCADE)
+ # zy: 启用状态字段 - 控制评论是否显示,用于评论审核机制
is_enable = models.BooleanField(_('enable'),
default=False, blank=False, null=False)
+ # zy: 模型元数据配置类 - 定义模型的数据库和行为配置
class Meta:
- ordering = ['-id']
- verbose_name = _('comment')
- verbose_name_plural = verbose_name
+ ordering = ['-id'] # zy: 默认排序规则 - 按ID降序排列,新的评论显示在前面
+ verbose_name = _('comment') # zy: 模型在Admin中的单数显示名称
+ verbose_name_plural = verbose_name # zy: 模型在Admin中的复数显示名称
get_latest_by = 'id'
def __str__(self):
diff --git a/src/DjangoBlog/comments/tests.py b/src/DjangoBlog/comments/tests.py
index 32d872c..406641e 100644
--- a/src/DjangoBlog/comments/tests.py
+++ b/src/DjangoBlog/comments/tests.py
@@ -1,6 +1,7 @@
+# zy: 评论模块测试文件 - 测试评论功能的各项业务逻辑
from django.test import Client, RequestFactory, TransactionTestCase
from django.urls import reverse
-
+# zy: 导入相关模型类,用于测试数据准备
from accounts.models import BlogUser
from blog.models import Category, Article
from comments.models import Comment
@@ -8,22 +9,26 @@ from comments.templatetags.comments_tags import *
from djangoblog.utils import get_max_articleid_commentid
-# Create your tests here.
+# zy: 评论功能测试类 - 测试评论的创建、验证、回复等功能
class CommentsTest(TransactionTestCase):
+ # zy: 测试初始化方法 - 在每个测试方法执行前运行,准备测试数据
def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
+ self.client = Client() # zy: 创建测试客户端,用于模拟HTTP请求
+ self.factory = RequestFactory() # zy: 创建请求工厂,用于构建请求对象
+ # zy: 配置博客设置,设置评论需要审核
from blog.models import BlogSettings
value = BlogSettings()
value.comment_need_review = True
value.save()
+ # zy: 创建测试超级用户,用于登录和权限测试
self.user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
+ # zy: 辅助方法 - 更新文章评论的审核状态为已通过
@staticmethod
def update_article_comment_status(article):
comments = article.comment_set.all()
@@ -31,80 +36,110 @@ class CommentsTest(TransactionTestCase):
comment.is_enable = True
comment.save()
+ # zy: 测试评论验证功能 - 测试评论的创建、回复和显示
def test_validate_comment(self):
self.client.login(username='liangliangyy1', password='liangliangyy1')
+ # zy: 创建测试分类
category = Category()
category.name = "categoryccc"
category.save()
+ # zy: 创建测试文章
article = Article()
article.title = "nicetitleccc"
article.body = "nicecontentccc"
- article.author = self.user
- article.category = category
- article.type = 'a'
- article.status = 'p'
+ article.author = self.user # zy: 设置文章作者为测试用户
+ article.category = category # zy: 设置文章分类
+ article.type = 'a' # zy: 设置文章类型
+ article.status = 'p' # zy: 设置文章状态为发布
article.save()
+ # zy: 获取发表评论的URL地址
comment_url = reverse(
'comments:postcomment', kwargs={
'article_id': article.id})
+ # zy: 测试1: 发表第一条评论
response = self.client.post(comment_url,
{
- 'body': '123ffffffffff'
+ 'body': '123ffffffffff' # zy: 评论内容
})
+ # zy: 断言响应状态码为302(重定向),表示评论提交成功
self.assertEqual(response.status_code, 302)
+ # zy: 重新从数据库获取文章对象,确保获取最新数据
article = Article.objects.get(pk=article.pk)
+ # zy: 断言评论列表为空(因为评论需要审核,默认不显示)
self.assertEqual(len(article.comment_list()), 0)
+
+ # zy: 更新评论状态为已审核通过
self.update_article_comment_status(article)
+ # zy: 断言现在评论列表中有1条评论
self.assertEqual(len(article.comment_list()), 1)
+ # zy: 测试2: 发表第二条评论
response = self.client.post(comment_url,
{
- 'body': '123ffffffffff',
+ 'body': '123ffffffffff', # zy: 第二条评论内容
})
+ # zy: 断言响应状态码为302(重定向)
self.assertEqual(response.status_code, 302)
+ # zy: 重新获取文章并更新评论状态
article = Article.objects.get(pk=article.pk)
self.update_article_comment_status(article)
+ # zy: 断言现在有2条评论
self.assertEqual(len(article.comment_list()), 2)
+
+ # zy: 获取第一条评论的ID,用于回复测试
parent_comment_id = article.comment_list()[0].id
+ # zy: 测试3: 发表带格式的回复评论
response = self.client.post(comment_url,
{
'body': '''
- # Title1
+ # Title1
- ```python
- import os
- ```
+ ```python
+ import os
+ ```
- [url](https://www.lylinux.net/)
+ [url](https://www.lylinux.net/)
- [ddd](http://www.baidu.com)
+ [ddd](http://www.baidu.com)
- ''',
- 'parent_comment_id': parent_comment_id
+ ''', # zy: 测试包含Markdown格式的评论内容
+ 'parent_comment_id': parent_comment_id # zy: 设置父评论ID,表示这是回复
})
+ # zy: 断言响应状态码为302(重定向)
self.assertEqual(response.status_code, 302)
+
+ # zy: 更新评论状态并重新获取文章
self.update_article_comment_status(article)
article = Article.objects.get(pk=article.pk)
+ # zy: 断言现在有3条评论(2条顶级评论 + 1条回复)
self.assertEqual(len(article.comment_list()), 3)
+
+ # zy: 测试评论树解析功能
comment = Comment.objects.get(id=parent_comment_id)
+ # zy: 解析评论树结构,检查回复关系
tree = parse_commenttree(article.comment_list(), comment)
- self.assertEqual(len(tree), 1)
+ self.assertEqual(len(tree), 1) # zy: 断言评论树解析正确
+
+ # zy: 测试评论项显示功能
data = show_comment_item(comment, True)
- self.assertIsNotNone(data)
+ self.assertIsNotNone(data) # zy: 断言评论项数据不为空
+
+ # zy: 测试获取最大文章ID和评论ID的工具函数
s = get_max_articleid_commentid()
- self.assertIsNotNone(s)
+ self.assertIsNotNone(s) # zy: 断言返回值不为空
+ # zy: 测试评论邮件发送功能
from comments.utils import send_comment_email
- send_comment_email(comment)
+ send_comment_email(comment) # zy: 发送评论通知邮件
\ No newline at end of file
diff --git a/src/DjangoBlog/comments/urls.py b/src/DjangoBlog/comments/urls.py
index 7df3fab..8b922d4 100644
--- a/src/DjangoBlog/comments/urls.py
+++ b/src/DjangoBlog/comments/urls.py
@@ -1,11 +1,17 @@
+# zy: 评论模块URL配置 - 定义评论相关的URL路由和视图映射
from django.urls import path
+# zy: 导入评论视图模块,包含评论相关的视图类
from . import views
+# zy: 定义应用命名空间 - 用于URL反向解析时避免命名冲突
app_name = "comments"
+
+# zy: URL模式列表 - 定义评论模块的所有URL路由规则
urlpatterns = [
+ # zy: 发表评论URL路由 - 处理用户发表评论的请求
path(
- 'article//postcomment',
- views.CommentPostView.as_view(),
- name='postcomment'),
-]
+ 'article//postcomment', # zy: URL模式,包含文章ID参数
+ views.CommentPostView.as_view(), # zy: 关联的基于类的视图
+ name='postcomment'), # zy: URL名称,用于反向解析
+]
\ No newline at end of file
diff --git a/src/DjangoBlog/comments/utils.py b/src/DjangoBlog/comments/utils.py
index f01dba7..0f9397c 100644
--- a/src/DjangoBlog/comments/utils.py
+++ b/src/DjangoBlog/comments/utils.py
@@ -1,13 +1,14 @@
-import logging
+import logging# zy: 评论邮件通知模块 - 处理评论相关的邮件发送功能
from django.utils.translation import gettext_lazy as _
-from djangoblog.utils import get_current_site
+from djangoblog.utils import get_current_site# zy: 导入项目工具函数,用于获取站点信息和发送邮件
from djangoblog.utils import send_email
+# zy: 创建日志记录器,用于记录邮件发送过程中的错误
logger = logging.getLogger(__name__)
-
+# zy: 发送评论邮件函数 - 处理评论发表和回复的邮件通知
def send_comment_email(comment):
site = get_current_site().domain
subject = _('Thanks for your comment')
diff --git a/src/DjangoBlog/comments/views.py b/src/DjangoBlog/comments/views.py
index ad9b2b9..6bae385 100644
--- a/src/DjangoBlog/comments/views.py
+++ b/src/DjangoBlog/comments/views.py
@@ -1,4 +1,4 @@
-# Create your views here.
+# zy: 评论视图模块 - 处理评论相关的视图逻辑和请求处理
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
@@ -6,26 +6,30 @@ from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect
from django.views.generic.edit import FormView
+# zy: 导入相关模型类
from accounts.models import BlogUser
from blog.models import Article
from .forms import CommentForm
from .models import Comment
-
+# zy: 评论发表视图类 - 基于FormView处理评论提交
class CommentPostView(FormView):
form_class = CommentForm
template_name = 'blog/article_detail.html'
+ # zy: 添加CSRF保护装饰器,防止跨站请求伪造攻击
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
return super(CommentPostView, self).dispatch(*args, **kwargs)
+ # zy: 处理GET请求 - 当用户直接访问评论URL时重定向到文章页面
def get(self, request, *args, **kwargs):
article_id = self.kwargs['article_id']
article = get_object_or_404(Article, pk=article_id)
url = article.get_absolute_url()
return HttpResponseRedirect(url + "#comments")
+ # zy: 表单验证失败时的处理逻辑
def form_invalid(self, form):
article_id = self.kwargs['article_id']
article = get_object_or_404(Article, pk=article_id)
@@ -35,6 +39,7 @@ class CommentPostView(FormView):
'article': article
})
+ # zy: 表单验证成功时的处理逻辑 - 保存评论数据
def form_valid(self, form):
"""提交的数据验证合法后的逻辑"""
user = self.request.user
@@ -42,6 +47,7 @@ class CommentPostView(FormView):
article_id = self.kwargs['article_id']
article = get_object_or_404(Article, pk=article_id)
+ # zy: 检查文章是否允许评论
if article.comment_status == 'c' or article.status == 'c':
raise ValidationError("该文章评论已关闭.")
comment = form.save(False)
@@ -52,6 +58,7 @@ class CommentPostView(FormView):
comment.is_enable = True
comment.author = author
+ # zy: 处理评论回复逻辑
if form.cleaned_data['parent_comment_id']:
parent_comment = Comment.objects.get(
pk=form.cleaned_data['parent_comment_id'])