/postcomment', # URL路径,包含文章ID参数(整数类型)
+ views.CommentPostView.as_view(), # 关联的视图类,使用as_view()方法转换为可调用视图
+ name='postcomment' # 该URL的名称,用于反向解析
+ ),
+]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/comments/utils.py b/src/DjangoBlog-master/comments/utils.py
index f01dba7..32de978 100644
--- a/src/DjangoBlog-master/comments/utils.py
+++ b/src/DjangoBlog-master/comments/utils.py
@@ -1,28 +1,45 @@
-import logging
+import logging # 导入日志模块,用于记录程序运行中的日志信息
-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 send_email
+from djangoblog.utils import get_current_site # 从自定义工具模块导入获取当前站点域名的函数
+from djangoblog.utils import send_email # 从自定义工具模块导入发送邮件的函数
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) # 创建当前模块的日志记录器,用于记录该模块的日志
def send_comment_email(comment):
+ """
+ 发送评论相关邮件:
+ 1. 向评论作者发送评论成功的感谢邮件
+ 2. 若当前评论是回复(有父评论),向父评论作者发送回复通知邮件
+ """
+ # 获取当前网站的域名(用于拼接文章链接)
site = get_current_site().domain
+ # 邮件主题:评论感谢(支持国际化)
subject = _('Thanks for your comment')
+ # 拼接评论对应的文章访问链接(HTTPS协议)
article_url = f"https://{site}{comment.article.get_absolute_url()}"
+ # 构建给评论作者的HTML格式邮件内容(支持国际化,通过占位符注入动态数据)
html_content = _("""Thank you very much for your comments on this site
You can visit %(article_title)s
to review your comments,
Thank you again!
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
+ # 调用发送邮件函数,向评论作者发送感谢邮件
send_email([tomail], subject, html_content)
+
try:
+ # 判断当前评论是否有父评论(即是否是回复评论)
if comment.parent_comment:
+ # 构建给父评论作者的HTML格式邮件内容(回复通知,支持国际化)
html_content = _("""Your comment on %(article_title)s
has
received a reply.
%(comment_body)s
@@ -30,9 +47,16 @@ def send_comment_email(comment):
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,
- 'comment_body': comment.parent_comment.body}
+ """) % {
+ 'article_url': article_url, # 文章访问链接
+ 'article_title': comment.article.title, # 文章标题
+ 'comment_body': comment.parent_comment.body # 父评论的内容(供作者识别)
+ }
+ # 父评论作者的邮箱(收件人)
tomail = comment.parent_comment.author.email
+ # 调用发送邮件函数,向父评论作者发送回复通知邮件
send_email([tomail], subject, html_content)
+ # 捕获发送回复邮件过程中的异常(避免单个邮件发送失败影响整体流程)
except Exception as e:
- logger.error(e)
+ # 记录异常日志(便于问题排查)
+ logger.error(e)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/comments/views.py b/src/DjangoBlog-master/comments/views.py
index ad9b2b9..57ffd52 100644
--- a/src/DjangoBlog-master/comments/views.py
+++ b/src/DjangoBlog-master/comments/views.py
@@ -1,63 +1,76 @@
# Create your views here.
-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.core.exceptions import ValidationError # 导入验证异常类,用于处理验证错误
+from django.http import HttpResponseRedirect # 导入HTTP重定向类,用于页面跳转
+from django.shortcuts import get_object_or_404 # 导入获取对象或返回404的工具函数
+from django.utils.decorators import method_decorator # 导入方法装饰器工具,用于为类视图方法添加装饰器
+from django.views.decorators.csrf import csrf_protect # 导入CSRF保护装饰器,防止跨站请求伪造
+from django.views.generic.edit import FormView # 导入表单视图基类,用于处理表单提交逻辑
-from accounts.models import BlogUser
-from blog.models import Article
-from .forms import CommentForm
-from .models import Comment
+from accounts.models import BlogUser # 从accounts应用导入用户模型
+from blog.models import Article # 从blog应用导入文章模型
+from .forms import CommentForm # 从当前应用导入评论表单
+from .models import Comment # 从当前应用导入评论模型
class CommentPostView(FormView):
- form_class = CommentForm
- template_name = 'blog/article_detail.html'
+ """评论提交视图类,处理评论发布功能"""
+ form_class = CommentForm # 指定使用的表单类为CommentForm
+ template_name = 'blog/article_detail.html' # 指定表单验证失败时渲染的模板
- @method_decorator(csrf_protect)
+ @method_decorator(csrf_protect) # 为dispatch方法添加CSRF保护
def dispatch(self, *args, **kwargs):
+ # 调用父类的dispatch方法,处理请求分发
return super(CommentPostView, self).dispatch(*args, **kwargs)
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")
+ """处理GET请求:重定向到文章详情页的评论区"""
+ article_id = self.kwargs['article_id'] # 从URL参数中获取文章ID
+ article = get_object_or_404(Article, pk=article_id) # 获取对应的文章对象,不存在则返回404
+ url = article.get_absolute_url() # 获取文章的绝对URL
+ return HttpResponseRedirect(url + "#comments") # 重定向到文章详情页的评论区锚点
def form_invalid(self, form):
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
+ """处理表单验证失败的情况"""
+ article_id = self.kwargs['article_id'] # 获取文章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):
- """提交的数据验证合法后的逻辑"""
- user = self.request.user
- author = BlogUser.objects.get(pk=user.pk)
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
+ """处理表单验证通过后的逻辑:保存评论并跳转"""
+ user = self.request.user # 获取当前登录用户
+ author = BlogUser.objects.get(pk=user.pk) # 获取用户对应的BlogUser对象
+ article_id = self.kwargs['article_id'] # 获取文章ID
+ article = get_object_or_404(Article, pk=article_id) # 获取文章对象
+ # 检查文章是否允许评论(评论状态为关闭或文章状态为草稿则不允许评论)
if article.comment_status == 'c' or article.status == 'c':
- raise ValidationError("该文章评论已关闭.")
- comment = form.save(False)
- comment.article = article
+ raise ValidationError("该文章评论已关闭.") # 抛出验证异常
+
+ comment = form.save(False) # 不立即保存表单数据,返回评论对象
+ comment.article = article # 设置评论关联的文章
+
+ # 获取博客设置,判断评论是否需要审核
from djangoblog.utils import get_blog_setting
settings = get_blog_setting()
- if not settings.comment_need_review:
- comment.is_enable = True
- comment.author = author
+ if not settings.comment_need_review: # 如果不需要审核
+ comment.is_enable = True # 直接设置评论为启用状态
+ comment.author = author # 设置评论的作者
+
+ # 处理回复功能:如果存在父评论ID,则设置父评论
if form.cleaned_data['parent_comment_id']:
parent_comment = Comment.objects.get(
- pk=form.cleaned_data['parent_comment_id'])
- comment.parent_comment = parent_comment
+ pk=form.cleaned_data['parent_comment_id']) # 获取父评论对象
+ comment.parent_comment = parent_comment # 设置当前评论的父评论
+
+ comment.save(True) # 保存评论到数据库
- comment.save(True)
+ # 重定向到文章详情页的当前评论位置(带锚点)
return HttpResponseRedirect(
"%s#div-comment-%d" %
- (article.get_absolute_url(), comment.pk))
+ (article.get_absolute_url(), comment.pk)) # 拼接URL,包含评论ID锚点
diff --git a/src/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml b/src/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml
index 83e35ff..764aa81 100644
--- a/src/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml
+++ b/src/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml
@@ -1,48 +1,52 @@
+# Docker Compose配置文件,版本为3(指定兼容的Compose语法版本)
version: '3'
+# 定义所有服务(容器)
services:
+ # 1. Elasticsearch服务(用于全文搜索功能,集成IK中文分词器)
es:
- image: liangliangyy/elasticsearch-analysis-ik:8.6.1
- container_name: es
- restart: always
- environment:
- - discovery.type=single-node
- - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- ports:
+ image: liangliangyy/elasticsearch-analysis-ik:8.6.1 # 使用带IK分词器的ES镜像,版本8.6.1
+ container_name: es # 容器名称固定为"es",便于管理
+ restart: always # 容器退出后自动重启(确保服务持续运行)
+ environment: # 环境变量配置
+ - discovery.type=single-node # 单节点模式(无需集群,适合测试/小型部署)
+ - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # 设置JVM内存大小(初始/最大均为512M,避免内存溢出)
+ ports: # 端口映射:主机9200端口 → 容器9200端口(ES默认API端口)
- 9200:9200
- volumes:
- - ./bin/datas/es/:/usr/share/elasticsearch/data/
+ volumes: # 数据卷挂载:持久化ES数据
+ - ./bin/datas/es/:/usr/share/elasticsearch/data/ # 主机目录 → 容器内ES数据存储目录
+ # 2. Kibana服务(ES的可视化管理工具,用于操作/监控ES)
kibana:
- image: kibana:8.6.1
- restart: always
- container_name: kibana
- ports:
+ image: kibana:8.6.1 # Kibana镜像,版本需与ES一致(8.6.1)
+ restart: always # 容器退出后自动重启
+ container_name: kibana # 容器名称固定为"kibana"
+ ports: # 端口映射:主机5601端口 → 容器5601端口(Kibana默认Web端口)
- 5601:5601
- environment:
- - ELASTICSEARCH_HOSTS=http://es:9200
+ environment: # 环境变量配置:指定关联的ES地址
+ - ELASTICSEARCH_HOSTS=http://es:9200 # 指向同网络内的"es"服务(容器间通过服务名通信)
+ # 3. Django博客服务(核心应用服务)
djangoblog:
- build: .
- restart: always
- command: bash -c 'sh /code/djangoblog/bin/docker_start.sh'
- ports:
+ build: . # 基于当前目录的Dockerfile构建镜像(不使用现成镜像,需本地有Dockerfile)
+ restart: always # 容器退出后自动重启
+ command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' # 容器启动后执行的命令:运行启动脚本
+ ports: # 端口映射:主机8000端口 → 容器8000端口(Django默认开发服务器端口)
- "8000:8000"
- volumes:
- - ./collectedstatic:/code/djangoblog/collectedstatic
- - ./uploads:/code/djangoblog/uploads
- environment:
- - DJANGO_MYSQL_DATABASE=djangoblog
- - DJANGO_MYSQL_USER=root
- - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E
- - DJANGO_MYSQL_HOST=db
- - DJANGO_MYSQL_PORT=3306
- - DJANGO_MEMCACHED_LOCATION=memcached:11211
- - DJANGO_ELASTICSEARCH_HOST=es:9200
- links:
+ volumes: # 数据卷挂载:持久化应用数据/静态资源
+ - ./collectedstatic:/code/djangoblog/collectedstatic # 主机静态资源目录 → 容器内静态资源目录(Nginx可直接访问)
+ - ./uploads:/code/djangoblog/uploads # 主机上传文件目录 → 容器内上传文件目录(如博客图片)
+ environment: # 环境变量配置:Django应用的关键参数(数据库、缓存、ES等)
+ - DJANGO_MYSQL_DATABASE=djangoblog # Django连接的MySQL数据库名
+ - DJANGO_MYSQL_USER=root # MySQL用户名
+ - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E # MySQL密码
+ - DJANGO_MYSQL_HOST=db # MySQL服务地址(指向同网络内的"db"服务,需额外配置db服务)
+ - DJANGO_MYSQL_PORT=3306 # MySQL端口
+ - DJANGO_MEMCACHED_LOCATION=memcached:11211 # Memcached缓存地址(指向同网络内的"memcached"服务,需额外配置)
+ - DJANGO_ELASTICSEARCH_HOST=es:9200 # ES服务地址(指向同网络内的"es"服务)
+ links: # 显式链接到其他服务(已逐步被depends_on替代,此处用于兼容)
+ - db # 链接到MySQL服务
+ - memcached # 链接到Memcached服务
+ depends_on: # 服务依赖:启动djangoblog前,先启动db服务(确保数据库就绪)
- db
- - memcached
- depends_on:
- - db
- container_name: djangoblog
-
+ container_name: djangoblog # 容器名称固定为"djangoblog"
\ No newline at end of file
diff --git a/src/DjangoBlog-master/deploy/docker-compose/docker-compose.yml b/src/DjangoBlog-master/deploy/docker-compose/docker-compose.yml
index 9609af3..902b118 100644
--- a/src/DjangoBlog-master/deploy/docker-compose/docker-compose.yml
+++ b/src/DjangoBlog-master/deploy/docker-compose/docker-compose.yml
@@ -1,60 +1,67 @@
+# Docker Compose配置文件,版本为3(指定Compose语法版本)
version: '3'
+# 定义所有服务(容器)
services:
+ # 1. MySQL数据库服务(存储应用数据)
db:
- image: mysql:latest
- restart: always
- environment:
- - MYSQL_DATABASE=djangoblog
- - MYSQL_ROOT_PASSWORD=DjAnGoBlOg!2!Q@W#E
- ports:
+ image: mysql:latest # 使用最新版MySQL镜像
+ restart: always # 容器退出后自动重启(确保服务持续运行)
+ environment: # 环境变量配置(数据库初始化参数)
+ - MYSQL_DATABASE=djangoblog # 自动创建的数据库名称
+ - MYSQL_ROOT_PASSWORD=DjAnGoBlOg!2!Q@W#E # MySQL root用户密码
+ ports: # 端口映射:主机3306端口 → 容器3306端口(MySQL默认端口)
- 3306:3306
- volumes:
- - ./bin/datas/mysql/:/var/lib/mysql
- depends_on:
+ volumes: # 数据卷挂载:持久化MySQL数据
+ - ./bin/datas/mysql/:/var/lib/mysql # 主机目录 → 容器内MySQL数据存储目录
+ depends_on: # 服务依赖:启动db前先启动redis(可能用于数据库缓存等场景)
- redis
- container_name: db
+ container_name: db # 容器名称固定为"db"
+ # 2. Django博客应用服务(核心应用)
djangoblog:
- build:
- context: ../../
- restart: always
- command: bash -c 'sh /code/djangoblog/bin/docker_start.sh'
- ports:
+ build: # 构建配置
+ context: ../../ # 指定Dockerfile所在的上下文目录(上级目录的上级目录)
+ restart: always # 容器退出后自动重启
+ command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' # 启动命令:执行应用启动脚本
+ ports: # 端口映射:主机8000端口 → 容器8000端口(Django应用端口)
- "8000:8000"
- volumes:
- - ./collectedstatic:/code/djangoblog/collectedstatic
- - ./logs:/code/djangoblog/logs
- - ./uploads:/code/djangoblog/uploads
- environment:
- - DJANGO_MYSQL_DATABASE=djangoblog
- - DJANGO_MYSQL_USER=root
- - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E
- - DJANGO_MYSQL_HOST=db
- - DJANGO_MYSQL_PORT=3306
- - DJANGO_REDIS_URL=redis:6379
- links:
+ volumes: # 数据卷挂载:持久化应用数据和配置
+ - ./collectedstatic:/code/djangoblog/collectedstatic # 静态资源目录(供Nginx访问)
+ - ./logs:/code/djangoblog/logs # 应用日志目录
+ - ./uploads:/code/djangoblog/uploads # 用户上传文件目录(如图片)
+ environment: # 环境变量配置(应用连接参数)
+ - DJANGO_MYSQL_DATABASE=djangoblog # 数据库名称(与db服务对应)
+ - DJANGO_MYSQL_USER=root # 数据库用户名
+ - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E # 数据库密码(与db服务对应)
+ - DJANGO_MYSQL_HOST=db # 数据库服务地址(指向同网络内的"db"服务)
+ - DJANGO_MYSQL_PORT=3306 # 数据库端口
+ - DJANGO_REDIS_URL=redis:6379 # Redis服务地址(指向同网络内的"redis"服务)
+ links: # 显式链接到其他服务(用于容器间通信)
+ - db # 链接到MySQL服务
+ - redis # 链接到Redis服务
+ depends_on: # 服务依赖:启动djangoblog前先启动db服务(确保数据库就绪)
- db
- - redis
- depends_on:
- - db
- container_name: djangoblog
+ container_name: djangoblog # 容器名称固定为"djangoblog"
+
+ # 3. Nginx服务(反向代理和静态资源服务)
nginx:
- restart: always
- image: nginx:latest
- ports:
+ restart: always # 容器退出后自动重启
+ image: nginx:latest # 使用最新版Nginx镜像
+ ports: # 端口映射:HTTP(80)和HTTPS(443)端口
- "80:80"
- "443:443"
- volumes:
- - ./bin/nginx.conf:/etc/nginx/nginx.conf
- - ./collectedstatic:/code/djangoblog/collectedstatic
- links:
- - djangoblog:djangoblog
- container_name: nginx
+ volumes: # 数据卷挂载:Nginx配置和静态资源
+ - ./bin/nginx.conf:/etc/nginx/nginx.conf # 主机Nginx配置文件 → 容器内Nginx配置文件
+ - ./collectedstatic:/code/djangoblog/collectedstatic # 静态资源目录(与djangoblog服务共享)
+ links: # 链接到djangoblog服务,实现反向代理
+ - djangoblog:djangoblog # 将djangoblog服务映射为"djangoblog"主机名
+ container_name: nginx # 容器名称固定为"nginx"
+ # 4. Redis服务(缓存服务,用于提升应用性能)
redis:
- restart: always
- image: redis:latest
- container_name: redis
- ports:
- - "6379:6379"
+ restart: always # 容器退出后自动重启
+ image: redis:latest # 使用最新版Redis镜像
+ container_name: redis # 容器名称固定为"redis"
+ ports: # 端口映射:主机6379端口 → 容器6379端口(Redis默认端口)
+ - "6379:6379"
\ No newline at end of file