周俊杰 1 month ago
commit 1efbacc3a7

@ -1,49 +1,79 @@
# 模块级注释Django管理后台配置模块 - 评论管理
# 本模块定义了评论模型在Django管理后台的显示配置和操作功能
from django.contrib import admin from django.contrib import admin
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# 函数级注释:禁用评论状态操作
# 管理员动作函数,用于批量禁用选中的评论
def disable_commentstatus(modeladmin, request, queryset): def disable_commentstatus(modeladmin, request, queryset):
# 核心代码将查询集中所有评论的is_enable字段更新为False
queryset.update(is_enable=False) queryset.update(is_enable=False)
# 函数级注释:启用评论状态操作
# 管理员动作函数,用于批量启用选中的评论
def enable_commentstatus(modeladmin, request, queryset): def enable_commentstatus(modeladmin, request, queryset):
# 核心代码将查询集中所有评论的is_enable字段更新为True
queryset.update(is_enable=True) queryset.update(is_enable=True)
# 设置动作函数的显示名称(国际化)
disable_commentstatus.short_description = _('Disable comments') disable_commentstatus.short_description = _('Disable comments')
enable_commentstatus.short_description = _('Enable comments') enable_commentstatus.short_description = _('Enable comments')
# 类级注释:评论管理类
# 继承自admin.ModelAdmin自定义评论模型在Django管理后台的显示和行为
class CommentAdmin(admin.ModelAdmin): class CommentAdmin(admin.ModelAdmin):
# 每页显示记录数配置
list_per_page = 20 list_per_page = 20
# 列表页显示的字段配置
list_display = ( list_display = (
'id', 'id',
'body', 'body',
'link_to_userinfo', 'link_to_userinfo', # 自定义方法显示用户链接
'link_to_article', 'link_to_article', # 自定义方法显示文章链接
'is_enable', 'is_enable',
'creation_time') 'creation_time')
# 可点击进入编辑页的字段
list_display_links = ('id', 'body', 'is_enable') list_display_links = ('id', 'body', 'is_enable')
# 右侧过滤器配置
list_filter = ('is_enable',) list_filter = ('is_enable',)
# 编辑页排除的字段(不显示)
exclude = ('creation_time', 'last_modify_time') exclude = ('creation_time', 'last_modify_time')
# 批量操作动作列表
actions = [disable_commentstatus, enable_commentstatus] actions = [disable_commentstatus, enable_commentstatus]
# 使用原始ID输入框的外键字段提升大表性能
raw_id_fields = ('author', 'article') raw_id_fields = ('author', 'article')
# 搜索字段配置
search_fields = ('body',) search_fields = ('body',)
# 方法级注释:用户信息链接显示
# 自定义方法,在列表页显示带链接的用户信息
def link_to_userinfo(self, obj): def link_to_userinfo(self, obj):
# 核心代码获取用户模型的app_label和model_name
info = (obj.author._meta.app_label, obj.author._meta.model_name) info = (obj.author._meta.app_label, obj.author._meta.model_name)
# 核心代码生成用户编辑页面的URL
link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
# 核心代码返回带HTML链接的格式化字符串
return format_html( return format_html(
u'<a href="%s">%s</a>' % u'<a href="%s">%s</a>' %
(link, obj.author.nickname if obj.author.nickname else obj.author.email)) (link, obj.author.nickname if obj.author.nickname else obj.author.email))
# 方法级注释:文章链接显示
# 自定义方法,在列表页显示带链接的文章标题
def link_to_article(self, obj): def link_to_article(self, obj):
# 核心代码获取文章模型的app_label和model_name
info = (obj.article._meta.app_label, obj.article._meta.model_name) info = (obj.article._meta.app_label, obj.article._meta.model_name)
# 核心代码生成文章编辑页面的URL
link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,))
# 核心代码返回带HTML链接的格式化字符串
return format_html( return format_html(
u'<a href="%s">%s</a>' % (link, obj.article.title)) u'<a href="%s">%s</a>' % (link, obj.article.title))
# 设置自定义方法在列表页的显示名称(国际化)
link_to_userinfo.short_description = _('User') link_to_userinfo.short_description = _('User')
link_to_article.short_description = _('Article') link_to_article.short_description = _('Article')

@ -1,5 +1,11 @@
# 模块级注释Django应用配置模块
# 本模块定义了comments应用的配置信息用于Django应用注册和初始化设置
from django.apps import AppConfig from django.apps import AppConfig
# 类级注释:评论应用配置类
# 继承自AppConfig用于配置comments应用的基本信息和启动行为
class CommentsConfig(AppConfig): class CommentsConfig(AppConfig):
# 应用名称字段定义应用的完整Python路径
# 此名称用于Django内部识别和应用引用
name = 'comments' name = 'comments'

@ -1,13 +1,24 @@
# 模块级注释Django表单定义模块 - 评论功能
# 本模块定义了评论相关的表单类,用于前端评论数据的验证和处理
from django import forms from django import forms
from django.forms import ModelForm from django.forms import ModelForm
# 导入评论模型,用于构建模型表单
from .models import Comment from .models import Comment
# 类级注释:评论表单类
# 继承自ModelForm基于Comment模型自动生成表单字段和验证规则
class CommentForm(ModelForm): class CommentForm(ModelForm):
# 父级评论ID字段隐藏输入字段用于处理评论回复功能
# 存储被回复评论的ID用户不可见但表单会处理
parent_comment_id = forms.IntegerField( parent_comment_id = forms.IntegerField(
widget=forms.HiddenInput, required=False) widget=forms.HiddenInput, required=False)
# 元数据类:配置模型表单的基本行为
class Meta: class Meta:
# 指定关联的模型Comment模型
model = Comment model = Comment
# 定义表单中包含的字段:只包含评论正文字段
# 其他字段如作者、文章等通过其他方式自动设置
fields = ['body'] fields = ['body']

@ -1,37 +1,65 @@
# Generated by Django 4.1.7 on 2023-03-02 07:14 # Generated by Django 4.1.7 on 2023-03-02 07:14
# 模块级注释Django数据库迁移文件
# 本模块定义了评论功能的数据库迁移操作,包括创建评论表和相关字段
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
# 类级注释:数据库迁移类
# 继承自migrations.Migration定义数据库结构变更的完整操作序列
class Migration(migrations.Migration): class Migration(migrations.Migration):
# 标记为初始迁移
# 表示这是comments应用的第一个迁移文件
initial = True initial = True
# 依赖关系定义
# 指定本迁移执行前需要先完成的依赖迁移
dependencies = [ dependencies = [
# 依赖blog应用的初始迁移确保文章表已创建
('blog', '0001_initial'), ('blog', '0001_initial'),
# 依赖用户模型迁移,确保用户表已存在
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
# 迁移操作列表
# 按顺序执行的数据库操作集合
operations = [ operations = [
# 创建模型操作
# 定义Comment模型的数据库表结构
migrations.CreateModel( migrations.CreateModel(
name='Comment', name='Comment',
fields=[ fields=[
# 主键字段自增BigAutoField作为评论的唯一标识
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 正文字段存储评论内容限制最大长度300字符
('body', models.TextField(max_length=300, verbose_name='正文')), ('body', models.TextField(max_length=300, verbose_name='正文')),
# 创建时间字段:记录评论创建时间,默认使用当前时区时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 修改时间字段:记录评论最后修改时间,默认使用当前时区时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
# 启用状态字段控制评论是否显示布尔类型默认True
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
# 外键字段:关联到文章模型,级联删除确保数据一致性
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')), ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')),
# 外键字段:关联到用户模型,记录评论作者
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
# 自关联外键:支持评论回复功能,允许空值表示顶级评论
('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')), ('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')),
], ],
# 模型元选项配置
# 定义模型在admin中的显示名称和默认排序等行为
options={ options={
# 单数显示名称在Django admin中显示的单数名称
'verbose_name': '评论', 'verbose_name': '评论',
# 复数显示名称在Django admin中显示的复数名称
'verbose_name_plural': '评论', 'verbose_name_plural': '评论',
# 默认排序按ID倒序排列最新评论显示在最前面
'ordering': ['-id'], 'ordering': ['-id'],
# 最新记录定义指定按id字段获取最新记录
'get_latest_by': 'id', 'get_latest_by': 'id',
}, },
), ),

@ -1,18 +1,34 @@
# Generated by Django 4.1.7 on 2023-04-24 13:48 # Generated by Django 4.1.7 on 2023-04-24 13:48
# 模块级注释Django数据库迁移文件 - 评论功能字段修改
# 本模块用于修改评论表中is_enable字段的默认值配置
from django.db import migrations, models from django.db import migrations, models
# 类级注释:数据库迁移类
# 继承自migrations.Migration定义对现有数据库结构的修改操作
class Migration(migrations.Migration): class Migration(migrations.Migration):
# 依赖关系定义
# 指定本迁移依赖于comments应用的初始迁移文件
dependencies = [ dependencies = [
# 依赖comments应用的0001_initial迁移
# 确保评论表已创建后再执行本迁移
('comments', '0001_initial'), ('comments', '0001_initial'),
] ]
# 迁移操作列表
# 包含对数据库结构的具体修改操作
operations = [ operations = [
# 修改字段操作
# 对Comment模型的is_enable字段进行配置修改
migrations.AlterField( migrations.AlterField(
# 指定要修改的模型名称
model_name='comment', model_name='comment',
# 指定要修改的字段名称
name='is_enable', name='is_enable',
# 新的字段配置将默认值从True改为False
# 新创建的评论默认不显示,需要手动启用
field=models.BooleanField(default=False, verbose_name='是否显示'), field=models.BooleanField(default=False, verbose_name='是否显示'),
), ),
] ]

@ -1,57 +1,86 @@
# Generated by Django 4.2.5 on 2023-09-06 13:13 # Generated by Django 4.2.5 on 2023-09-06 13:13
# 模块级注释Django数据库迁移文件 - 评论模型字段重构
# 本模块对评论模型进行重大重构,包括字段重命名、显示名称国际化等操作
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
# 类级注释:数据库迁移类
# 继承自migrations.Migration定义对评论模型的多个结构变更操作
class Migration(migrations.Migration): class Migration(migrations.Migration):
# 依赖关系定义
# 指定本迁移执行前需要完成的依赖迁移文件
dependencies = [ dependencies = [
# 依赖用户模型迁移,确保用户表结构就绪
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
# 依赖博客应用的第5次迁移确保文章表结构稳定
('blog', '0005_alter_article_options_alter_category_options_and_more'), ('blog', '0005_alter_article_options_alter_category_options_and_more'),
# 依赖评论应用的第2次迁移确保之前的字段修改已应用
('comments', '0002_alter_comment_is_enable'), ('comments', '0002_alter_comment_is_enable'),
] ]
# 迁移操作列表
# 包含多个对评论模型的结构变更操作,按顺序执行
operations = [ operations = [
# 修改模型选项操作
# 更新Comment模型的元数据配置主要修改显示名称
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='comment', name='comment',
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'}, options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'},
), ),
# 删除字段操作
# 移除旧的创建时间字段,为新增字段做准备
migrations.RemoveField( migrations.RemoveField(
model_name='comment', model_name='comment',
name='created_time', name='created_time',
), ),
# 删除字段操作
# 移除旧的最后修改时间字段,为新增字段做准备
migrations.RemoveField( migrations.RemoveField(
model_name='comment', model_name='comment',
name='last_mod_time', name='last_mod_time',
), ),
# 新增字段操作
# 添加新的创建时间字段,使用更清晰的字段命名
migrations.AddField( migrations.AddField(
model_name='comment', model_name='comment',
name='creation_time', name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
), ),
# 新增字段操作
# 添加新的最后修改时间字段,使用更清晰的字段命名
migrations.AddField( migrations.AddField(
model_name='comment', model_name='comment',
name='last_modify_time', name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
), ),
# 修改字段操作
# 更新文章外键字段的显示名称,改为英文
migrations.AlterField( migrations.AlterField(
model_name='comment', model_name='comment',
name='article', name='article',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'),
), ),
# 修改字段操作
# 更新作者外键字段的显示名称,改为英文
migrations.AlterField( migrations.AlterField(
model_name='comment', model_name='comment',
name='author', name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
), ),
# 修改字段操作
# 更新启用状态字段的显示名称,改为英文
migrations.AlterField( migrations.AlterField(
model_name='comment', model_name='comment',
name='is_enable', name='is_enable',
field=models.BooleanField(default=False, verbose_name='enable'), field=models.BooleanField(default=False, verbose_name='enable'),
), ),
# 修改字段操作
# 更新父级评论外键字段的显示名称,改为英文
migrations.AlterField( migrations.AlterField(
model_name='comment', model_name='comment',
name='parent_comment', name='parent_comment',

@ -1,39 +1,56 @@
# 模块级注释Django数据模型模块 - 评论系统
# 本模块定义了评论系统的数据模型,包括评论的基本字段、关联关系和业务逻辑
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# 导入文章模型,用于建立评论与文章的关联
from blog.models import Article from blog.models import Article
# Create your models here. # 类级注释:评论模型类
# 继承自models.Model定义了评论数据的数据库表结构和字段约束
class Comment(models.Model): class Comment(models.Model):
# 正文字段存储评论内容最大长度300字符使用中文标签
body = models.TextField('正文', max_length=300) body = models.TextField('正文', max_length=300)
# 创建时间字段:自动记录评论创建时间,使用国际化标签
creation_time = models.DateTimeField(_('creation time'), default=now) creation_time = models.DateTimeField(_('creation time'), default=now)
# 最后修改时间字段:记录评论最后修改时间,使用国际化标签
last_modify_time = models.DateTimeField(_('last modify time'), default=now) last_modify_time = models.DateTimeField(_('last modify time'), default=now)
# 作者字段:外键关联到用户模型,级联删除,使用国际化标签
author = models.ForeignKey( author = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
verbose_name=_('author'), verbose_name=_('author'),
on_delete=models.CASCADE) on_delete=models.CASCADE)
# 文章字段:外键关联到文章模型,级联删除,使用国际化标签
article = models.ForeignKey( article = models.ForeignKey(
Article, Article,
verbose_name=_('article'), verbose_name=_('article'),
on_delete=models.CASCADE) on_delete=models.CASCADE)
# 父级评论字段:自关联外键,支持评论回复功能,允许空值
parent_comment = models.ForeignKey( parent_comment = models.ForeignKey(
'self', 'self',
verbose_name=_('parent comment'), verbose_name=_('parent comment'),
blank=True, blank=True,
null=True, null=True,
on_delete=models.CASCADE) on_delete=models.CASCADE)
# 启用状态字段:控制评论是否显示,默认不显示,使用国际化标签
is_enable = models.BooleanField(_('enable'), is_enable = models.BooleanField(_('enable'),
default=False, blank=False, null=False) default=False, blank=False, null=False)
# 元数据类配置模型在数据库和admin中的行为
class Meta: class Meta:
# 默认排序按ID倒序最新评论在前
ordering = ['-id'] ordering = ['-id']
# 单数显示名称:使用国际化翻译
verbose_name = _('comment') verbose_name = _('comment')
# 复数显示名称:与单数相同
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
# 最新记录定义按ID字段确定最新记录
get_latest_by = 'id' get_latest_by = 'id'
# 字符串表示方法定义对象在Python中的显示格式
def __str__(self): def __str__(self):
# 返回评论正文作为对象的字符串表示
return self.body return self.body

@ -1,68 +1,92 @@
# 模块级注释Django测试模块 - 评论系统功能测试
# 本模块包含评论系统的完整测试用例,验证评论发布、回复、显示等核心功能
from django.test import Client, RequestFactory, TransactionTestCase from django.test import Client, RequestFactory, TransactionTestCase
from django.urls import reverse from django.urls import reverse
# 导入相关模型,用于测试数据准备
from accounts.models import BlogUser from accounts.models import BlogUser
from blog.models import Category, Article from blog.models import Category, Article
from comments.models import Comment from comments.models import Comment
# 导入评论标签模块,测试模板标签功能
from comments.templatetags.comments_tags import * from comments.templatetags.comments_tags import *
from djangoblog.utils import get_max_articleid_commentid from djangoblog.utils import get_max_articleid_commentid
# Create your tests here. # 类级注释:评论系统测试类
# 继承自TransactionTestCase支持数据库事务的测试用例
class CommentsTest(TransactionTestCase): class CommentsTest(TransactionTestCase):
# 测试初始化方法:在每个测试方法执行前运行
def setUp(self): def setUp(self):
# 创建测试客户端用于模拟HTTP请求
self.client = Client() self.client = Client()
# 创建请求工厂,用于构建请求对象
self.factory = RequestFactory() self.factory = RequestFactory()
# 配置博客设置:启用评论审核功能
from blog.models import BlogSettings from blog.models import BlogSettings
value = BlogSettings() value = BlogSettings()
value.comment_need_review = True value.comment_need_review = True # 设置评论需要审核
value.save() value.save()
# 创建超级用户,用于测试认证相关的评论功能
self.user = BlogUser.objects.create_superuser( self.user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com", email="liangliangyy1@gmail.com",
username="liangliangyy1", username="liangliangyy1",
password="liangliangyy1") password="liangliangyy1")
# 辅助方法:更新文章评论状态为启用
def update_article_comment_status(self, article): def update_article_comment_status(self, article):
# 获取文章的所有评论
comments = article.comment_set.all() comments = article.comment_set.all()
# 遍历所有评论,将其状态设置为启用
for comment in comments: for comment in comments:
comment.is_enable = True comment.is_enable = True
comment.save() comment.save()
# 测试方法:验证评论功能
def test_validate_comment(self): def test_validate_comment(self):
# 使用测试用户登录系统
self.client.login(username='liangliangyy1', password='liangliangyy1') self.client.login(username='liangliangyy1', password='liangliangyy1')
# 创建测试分类
category = Category() category = Category()
category.name = "categoryccc" category.name = "categoryccc"
category.save() category.save()
# 创建测试文章
article = Article() article = Article()
article.title = "nicetitleccc" article.title = "nicetitleccc"
article.body = "nicecontentccc" article.body = "nicecontentccc"
article.author = self.user article.author = self.user
article.category = category article.category = category
article.type = 'a' article.type = 'a' # 文章类型
article.status = 'p' article.status = 'p' # 发布状态
article.save() article.save()
# 生成评论提交URL
comment_url = reverse( comment_url = reverse(
'comments:postcomment', kwargs={ 'comments:postcomment', kwargs={
'article_id': article.id}) 'article_id': article.id})
# 测试提交第一条评论
response = self.client.post(comment_url, response = self.client.post(comment_url,
{ {
'body': '123ffffffffff' 'body': '123ffffffffff'
}) })
# 验证响应状态码为302重定向
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
# 重新获取文章对象验证评论数量由于审核机制初始应为0
article = Article.objects.get(pk=article.pk) article = Article.objects.get(pk=article.pk)
self.assertEqual(len(article.comment_list()), 0) self.assertEqual(len(article.comment_list()), 0)
self.update_article_comment_status(article)
# 更新评论状态为启用后验证评论数量变为1
self.update_article_comment_status(article)
self.assertEqual(len(article.comment_list()), 1) self.assertEqual(len(article.comment_list()), 1)
# 测试提交第二条评论
response = self.client.post(comment_url, response = self.client.post(comment_url,
{ {
'body': '123ffffffffff', 'body': '123ffffffffff',
@ -70,11 +94,15 @@ class CommentsTest(TransactionTestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
# 验证第二条评论提交成功
article = Article.objects.get(pk=article.pk) article = Article.objects.get(pk=article.pk)
self.update_article_comment_status(article) self.update_article_comment_status(article)
self.assertEqual(len(article.comment_list()), 2) self.assertEqual(len(article.comment_list()), 2)
# 获取第一条评论的ID用于测试回复功能
parent_comment_id = article.comment_list()[0].id parent_comment_id = article.comment_list()[0].id
# 测试提交带Markdown格式的回复评论
response = self.client.post(comment_url, response = self.client.post(comment_url,
{ {
'body': ''' 'body': '''
@ -93,17 +121,25 @@ class CommentsTest(TransactionTestCase):
'parent_comment_id': parent_comment_id 'parent_comment_id': parent_comment_id
}) })
# 验证回复评论提交成功
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.update_article_comment_status(article) self.update_article_comment_status(article)
article = Article.objects.get(pk=article.pk) article = Article.objects.get(pk=article.pk)
self.assertEqual(len(article.comment_list()), 3) self.assertEqual(len(article.comment_list()), 3)
# 测试评论树解析功能
comment = Comment.objects.get(id=parent_comment_id) comment = Comment.objects.get(id=parent_comment_id)
tree = parse_commenttree(article.comment_list(), comment) tree = parse_commenttree(article.comment_list(), comment)
self.assertEqual(len(tree), 1) self.assertEqual(len(tree), 1)
# 测试评论项显示功能
data = show_comment_item(comment, True) data = show_comment_item(comment, True)
self.assertIsNotNone(data) self.assertIsNotNone(data)
# 测试获取最大文章ID和评论ID功能
s = get_max_articleid_commentid() s = get_max_articleid_commentid()
self.assertIsNotNone(s) self.assertIsNotNone(s)
# 测试评论邮件发送功能
from comments.utils import send_comment_email from comments.utils import send_comment_email
send_comment_email(comment) send_comment_email(comment)

@ -1,11 +1,23 @@
# 模块级注释Django URL配置模块 - 评论系统路由
# 本模块定义了评论系统的URL路由配置将URL路径映射到对应的视图函数
from django.urls import path from django.urls import path
# 导入当前应用的视图模块用于处理URL请求
from . import views from . import views
# 应用命名空间定义:设置评论应用的命名空间为"comments"
# 用于Django的URL反向解析避免不同应用间的URL名称冲突
app_name = "comments" app_name = "comments"
# URL模式列表定义评论系统的所有URL路由规则
urlpatterns = [ urlpatterns = [
# 评论提交路由:处理文章评论的提交请求
path( path(
# URL路径模式匹配/article/{文章ID}/postcomment格式的URL
# 其中<int:article_id>为路径参数捕获整数类型的文章ID
'article/<int:article_id>/postcomment', 'article/<int:article_id>/postcomment',
# 对应的视图类使用CommentPostView类视图处理该路径的请求
views.CommentPostView.as_view(), views.CommentPostView.as_view(),
# URL名称命名为'postcomment'用于在模板和代码中进行URL反向解析
name='postcomment'), name='postcomment'),
] ]

@ -1,17 +1,29 @@
# 模块级注释:评论邮件通知模块
# 本模块提供评论相关的邮件通知功能,包括新评论确认邮件和评论回复通知邮件
import logging import logging
# 导入Django国际化翻译模块
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# 导入工具函数:获取当前站点信息和发送邮件
from djangoblog.utils import get_current_site from djangoblog.utils import get_current_site
from djangoblog.utils import send_email from djangoblog.utils import send_email
# 创建日志记录器实例,用于记录邮件发送过程中的错误信息
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 函数级注释:发送评论邮件通知
# 主要功能:向评论作者发送评论确认邮件,如果是对回复的评论,则同时向被回复者发送通知邮件
def send_comment_email(comment): def send_comment_email(comment):
# 获取当前站点域名,用于构建完整的文章链接
site = get_current_site().domain site = get_current_site().domain
# 邮件主题:使用国际化翻译
subject = _('Thanks for your comment') subject = _('Thanks for your comment')
# 构建完整的文章URL地址
article_url = f"https://{site}{comment.article.get_absolute_url()}" article_url = f"https://{site}{comment.article.get_absolute_url()}"
# 构建评论确认邮件的HTML内容
html_content = _("""<p>Thank you very much for your comments on this site</p> html_content = _("""<p>Thank you very much for your comments on this site</p>
You can visit <a href="%(article_url)s" rel="bookmark">%(article_title)s</a> You can visit <a href="%(article_url)s" rel="bookmark">%(article_title)s</a>
to review your comments, to review your comments,
@ -19,10 +31,17 @@ def send_comment_email(comment):
<br /> <br />
If the link above cannot be opened, please copy this link to your browser. 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)s""") % {'article_url': article_url, 'article_title': comment.article.title}
# 获取评论作者的邮箱地址
tomail = comment.author.email tomail = comment.author.email
# 发送评论确认邮件给评论作者
send_email([tomail], subject, html_content) send_email([tomail], subject, html_content)
# 异常处理块:处理评论回复通知邮件的发送
try: try:
# 检查是否存在父级评论(即当前评论是否为回复评论)
if comment.parent_comment: if comment.parent_comment:
# 构建回复通知邮件的HTML内容
html_content = _("""Your comment on <a href="%(article_url)s" rel="bookmark">%(article_title)s</a><br/> has html_content = _("""Your comment on <a href="%(article_url)s" rel="bookmark">%(article_title)s</a><br/> has
received a reply. <br/> %(comment_body)s received a reply. <br/> %(comment_body)s
<br/> <br/>
@ -32,7 +51,11 @@ def send_comment_email(comment):
%(article_url)s %(article_url)s
""") % {'article_url': article_url, 'article_title': comment.article.title, """) % {'article_url': article_url, 'article_title': comment.article.title,
'comment_body': comment.parent_comment.body} 'comment_body': comment.parent_comment.body}
# 获取被回复评论作者的邮箱地址
tomail = comment.parent_comment.author.email tomail = comment.parent_comment.author.email
# 发送回复通知邮件给被回复者
send_email([tomail], subject, html_content) send_email([tomail], subject, html_content)
# 捕获邮件发送过程中可能出现的任何异常
except Exception as e: except Exception as e:
# 记录异常信息到日志,但不中断程序执行
logger.error(e) logger.error(e)

@ -1,3 +1,5 @@
# 模块级注释Django视图模块 - 评论系统
# 本模块定义了评论提交的视图处理逻辑,包括评论验证、保存和重定向等功能
# Create your views here. # Create your views here.
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -6,58 +8,94 @@ from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
# 导入相关模型
from accounts.models import BlogUser from accounts.models import BlogUser
from blog.models import Article from blog.models import Article
from .forms import CommentForm from .forms import CommentForm
from .models import Comment from .models import Comment
# 类级注释:评论提交视图类
# 继承自FormView处理评论表单的提交和验证
class CommentPostView(FormView): class CommentPostView(FormView):
# 指定使用的表单类
form_class = CommentForm form_class = CommentForm
# 指定模板名称
template_name = 'blog/article_detail.html' template_name = 'blog/article_detail.html'
# 方法装饰器添加CSRF保护
@method_decorator(csrf_protect) @method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
# 调用父类的dispatch方法确保CSRF保护生效
return super(CommentPostView, self).dispatch(*args, **kwargs) return super(CommentPostView, self).dispatch(*args, **kwargs)
# GET请求处理方法
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
# 从URL参数获取文章ID
article_id = self.kwargs['article_id'] article_id = self.kwargs['article_id']
# 获取文章对象如果不存在返回404
article = get_object_or_404(Article, pk=article_id) article = get_object_or_404(Article, pk=article_id)
# 获取文章的绝对URL
url = article.get_absolute_url() url = article.get_absolute_url()
# 重定向到文章详情页的评论区域
return HttpResponseRedirect(url + "#comments") return HttpResponseRedirect(url + "#comments")
# 表单验证失败处理方法
def form_invalid(self, form): def form_invalid(self, form):
# 获取文章ID
article_id = self.kwargs['article_id'] article_id = self.kwargs['article_id']
# 获取文章对象
article = get_object_or_404(Article, pk=article_id) article = get_object_or_404(Article, pk=article_id)
# 重新渲染模板,显示表单错误信息
return self.render_to_response({ return self.render_to_response({
'form': form, 'form': form,
'article': article 'article': article
}) })
# 表单验证成功处理方法
def form_valid(self, form): def form_valid(self, form):
"""提交的数据验证合法后的逻辑""" """提交的数据验证合法后的逻辑"""
# 获取当前登录用户
user = self.request.user user = self.request.user
# 根据用户ID获取用户对象
author = BlogUser.objects.get(pk=user.pk) author = BlogUser.objects.get(pk=user.pk)
# 从URL参数获取文章ID
article_id = self.kwargs['article_id'] article_id = self.kwargs['article_id']
# 获取文章对象
article = get_object_or_404(Article, pk=article_id) article = get_object_or_404(Article, pk=article_id)
# 检查文章是否允许评论
if article.comment_status == 'c' or article.status == 'c': if article.comment_status == 'c' or article.status == 'c':
# 抛出验证异常:文章评论已关闭
raise ValidationError("该文章评论已关闭.") raise ValidationError("该文章评论已关闭.")
# 从表单获取评论对象但不保存到数据库
comment = form.save(False) comment = form.save(False)
# 设置评论关联的文章
comment.article = article comment.article = article
# 获取博客设置
from djangoblog.utils import get_blog_setting from djangoblog.utils import get_blog_setting
settings = get_blog_setting() settings = get_blog_setting()
# 如果设置不需要审核,直接启用评论
if not settings.comment_need_review: if not settings.comment_need_review:
comment.is_enable = True comment.is_enable = True
# 设置评论作者
comment.author = author comment.author = author
# 处理回复评论的情况
if form.cleaned_data['parent_comment_id']: if form.cleaned_data['parent_comment_id']:
# 获取父级评论对象
parent_comment = Comment.objects.get( parent_comment = Comment.objects.get(
pk=form.cleaned_data['parent_comment_id']) 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( return HttpResponseRedirect(
"%s#div-comment-%d" % "%s#div-comment-%d" %
(article.get_absolute_url(), comment.pk)) (article.get_absolute_url(), comment.pk))
Loading…
Cancel
Save