diff --git a/doc/report.md b/doc/report.md new file mode 100644 index 0000000..4fa3300 --- /dev/null +++ b/doc/report.md @@ -0,0 +1,26 @@ +# 项目开发报告 + +## 项目概述 +本项目是一个软件工程方法论实践项目。 + +## 登录功能开发进度 + +### 已完成 +- [x] 项目环境搭建 +- [x] Git分支管理配置 +- [x] 基础文档结构创建 + +### 进行中 +- [ ] 用户登录功能实现 +- [ ] 密码加密模块 +- [ ] 会话管理功能 + +### 技术栈 +- Python 3.x +- Git 版本控制 +- Markdown 文档 + +## 下一步计划 +1. 实现登录功能代码 +2. 编写单元测试 +3. 完成集成测试 \ No newline at end of file diff --git a/doc/“电影评价”软件系统的数据模型设计.docx b/doc/“电影评价”软件系统的数据模型设计.docx new file mode 100644 index 0000000..8d282e0 Binary files /dev/null and b/doc/“电影评价”软件系统的数据模型设计.docx differ diff --git a/doc/开源软件泛读报告.docx b/doc/开源软件泛读报告.docx new file mode 100644 index 0000000..720815c Binary files /dev/null and b/doc/开源软件泛读报告.docx differ diff --git a/doc/电影评价软件界面设计说明书模板.docx b/doc/电影评价软件界面设计说明书模板.docx new file mode 100644 index 0000000..3c9baa0 Binary files /dev/null and b/doc/电影评价软件界面设计说明书模板.docx differ diff --git a/src/DjangoBlog-master/.coveragerc b/src/DjangoBlog-master/.coveragerc index 9757484..06b8828 100644 --- a/src/DjangoBlog-master/.coveragerc +++ b/src/DjangoBlog-master/.coveragerc @@ -1,10 +1,12 @@ -[run] -source = . -include = *.py +[run] # 运行配置部分 +source = . # 指定要测量覆盖率的源代码根目录为当前目录 +include = *.py # 包含所有.py文件进行覆盖率测量 + +# 排除的文件和目录列表 omit = - *migrations* - *tests* - *.html - *whoosh_cn_backend* - *settings.py* - *venv* + *migrations* # 排除Django迁移文件 + *tests* # 排除测试文件 + *.html # 排除HTML模板文件 + *whoosh_cn_backend* # 排除中文搜索后端相关文件 + *settings.py* # 排除配置文件 + *venv* # 排除虚拟环境目录 \ No newline at end of file diff --git a/src/DjangoBlog-master/.dockerignore b/src/DjangoBlog-master/.dockerignore index 2818c38..c830766 100644 --- a/src/DjangoBlog-master/.dockerignore +++ b/src/DjangoBlog-master/.dockerignore @@ -1,11 +1,29 @@ +# 数据文件目录 - 通常包含动态生成的数据,不应纳入版本控制 bin/data/ -# virtualenv + +# Python 虚拟环境目录 - 包含项目依赖,应该通过 requirements.txt 管理 venv/ + +# Django 收集的静态文件目录 - 由 collectstatic 命令生成 collectedstatic/ + +# Whoosh 搜索引擎索引目录 - 包含搜索索引数据,会频繁变化 djangoblog/whoosh_index/ + +# 用户上传文件目录 - 包含用户上传的媒体文件 uploads/ + +# 生产环境配置文件 - 包含敏感信息如数据库密码、API密钥等 settings_production.py + +# 所有 Markdown 文档文件 - 可能是临时文件或本地笔记 *.md + +# 文档目录 - 可能包含生成的文档或本地文档 docs/ + +# 日志文件目录 - 包含应用程序运行日志 logs/ + +# 静态文件目录 - 可能包含前端构建产物或开发时的静态文件 static/ \ No newline at end of file diff --git a/src/DjangoBlog-master/.gitattributes b/src/DjangoBlog-master/.gitattributes index fd52ece..8cf12b4 100644 --- a/src/DjangoBlog-master/.gitattributes +++ b/src/DjangoBlog-master/.gitattributes @@ -1,6 +1,18 @@ +# 将 blog/static/ 目录下的所有文件标记为"vendored"(第三方代码) +# 这样 GitHub 的语言统计会忽略这些文件 blog/static/* linguist-vendored + +# 将所有 .js 文件标记为"vendored",在语言统计中忽略 *.js linguist-vendored + +# 将所有 .css 文件标记为"vendored",在语言统计中忽略 *.css linguist-vendored + +# 设置所有文件使用自动换行符检测 * text=auto + +# 设置 .sh 文件为文本文件,并强制使用 LF 换行符 *.sh text eol=lf + +# 设置 .conf 文件为文本文件,并强制使用 LF 换行符 *.conf text eol=lf \ No newline at end of file diff --git a/src/DjangoBlog-master/.gitignore b/src/DjangoBlog-master/.gitignore index 3015816..22a3ad6 100644 --- a/src/DjangoBlog-master/.gitignore +++ b/src/DjangoBlog-master/.gitignore @@ -1,80 +1,132 @@ +# ================================ +# Python 特定忽略规则 +# ================================ + # Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class +__pycache__/ # Python 字节码缓存目录 +*.py[cod] # 编译的 Python 文件:.pyc, .pyo, .pyd +*$py.class # Jython 编译文件 # C extensions -*.so +*.so # Python C 扩展模块 + +# ================================ +# 包管理和分发文件 +# ================================ # Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg +.Python # Python 环境相关 +env/ # 虚拟环境(通用名称) +build/ # 构建输出目录 +develop-eggs/ # 开发阶段的 egg 文件 +dist/ # 源码包分发目录 +downloads/ # 下载的依赖包 +eggs/ # Egg 安装目录 +.eggs/ # 隐藏的 egg 目录 +lib/ # 依赖库 +lib64/ # 64位依赖库 +parts/ # 部件目录 +sdist/ # 源码分发文件 +var/ # 变量数据 +*.egg-info/ # Egg 包信息 +.installed.cfg # 安装配置 +*.egg # Egg 包文件 + +# ================================ +# 打包工具相关 +# ================================ # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec +*.manifest # Windows 清单文件 +*.spec # PyInstaller 规范文件 + +# ================================ +# 安装和日志文件 +# ================================ # Installer logs -pip-log.txt -pip-delete-this-directory.txt +pip-log.txt # pip 安装日志 +pip-delete-this-directory.txt # pip 清理文件 + +# ================================ +# 测试和覆盖率报告 +# ================================ # Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover +htmlcov/ # HTML 覆盖率报告 +.tox/ # tox 测试环境 +.coverage # coverage.py 数据文件 +.coverage.* # coverage.py 临时文件 +.cache # 缓存目录 +nosetests.xml # nose 测试报告 +coverage.xml # XML 覆盖率报告 +*,cover # coverage.py 覆盖文件 + +# ================================ +# 国际化和文档 +# ================================ # Translations -*.pot +*.pot # Gettext 模板文件 + +# ================================ +# Django 特定文件 +# ================================ # Django stuff: -*.log -logs/ +*.log # 日志文件 +logs/ # 日志目录 # Sphinx documentation -docs/_build/ +docs/_build/ # Sphinx 文档构建输出 + +# ================================ +# 构建工具 +# ================================ # PyBuilder -target/ +target/ # PyBuilder 目标目录 +# ================================ +# IDE 和编辑器 +# ================================ # PyCharm # http://www.jetbrains.com/pycharm/webhelp/project.html -.idea -.iml -static/ +.idea # PyCharm 项目配置 +.iml # IntelliJ 模块文件 + +# ================================ +# 项目特定忽略规则 +# ================================ + +static/ # 开发静态文件(生产环境使用 collectedstatic) # virtualenv -venv/ - -collectedstatic/ -djangoblog/whoosh_index/ -google93fd32dbd906620a.html -baidu_verify_FlHL7cUyC9.html -BingSiteAuth.xml -cb9339dbe2ff86a5aa169d28dba5f615.txt -werobot_session.* -django.jpg -uploads/ -settings_production.py -werobot_session.db -bin/datas/ +venv/ # 虚拟环境目录 + +collectedstatic/ # Django collectstatic 收集的静态文件 +djangoblog/whoosh_index/ # Whoosh 搜索引擎索引目录 + +# 各种验证文件(搜索引擎、服务商验证) +google93fd32dbd906620a.html # Google 网站验证 +baidu_verify_FlHL7cUyC9.html # 百度验证 +BingSiteAuth.xml # Bing 网站认证 +cb9339dbe2ff86a5aa169d28dba5f615.txt # 未知验证文件 + +# 微信机器人会话 +werobot_session.* # WeRoBot 会话文件 + +# 媒体文件 +django.jpg # 可能的上传图片 +uploads/ # 用户上传文件目录 + +# 配置文件 +settings_production.py # 生产环境设置(包含敏感信息) + +# 数据库文件 +werobot_session.db # WeRoBot 会话数据库 + +# 数据文件 +bin/datas/ # 数据文件目录 \ No newline at end of file diff --git a/src/DjangoBlog-master/Dockerfile b/src/DjangoBlog-master/Dockerfile index 80b46ac..e545f07 100644 --- a/src/DjangoBlog-master/Dockerfile +++ b/src/DjangoBlog-master/Dockerfile @@ -1,15 +1,42 @@ +# 使用 Python 3.11 作为基础镜像 FROM python:3.11 + +# 设置环境变量,确保 Python 输出直接显示在容器日志中(不缓冲) ENV PYTHONUNBUFFERED 1 + +# 设置工作目录为 /code/djangoblog/ WORKDIR /code/djangoblog/ -RUN apt-get update && \ - apt-get install default-libmysqlclient-dev gettext -y && \ + +# 安装系统依赖包 +RUN apt-get update && \\ + apt-get install default-libmysqlclient-dev gettext -y && \\ rm -rf /var/lib/apt/lists/* +# 解释: +# - apt-get update: 更新包列表 +# - default-libmysqlclient-dev: MySQL 客户端开发库(用于 mysqlclient Python 包) +# - gettext: 国际化工具(用于 Django 的 makemessages 和 compilemessages) +# - rm -rf /var/lib/apt/lists/*: 清理 apt 缓存,减小镜像大小 + +# 添加 requirements.txt 文件到镜像中 ADD requirements.txt requirements.txt -RUN pip install --upgrade pip && \ - pip install --no-cache-dir -r requirements.txt && \ - pip install --no-cache-dir gunicorn[gevent] && \ + +# 安装 Python 依赖 +RUN pip install --upgrade pip && \\ + pip install --no-cache-dir -r requirements.txt && \\ + pip install --no-cache-dir gunicorn[gevent] && \\ pip cache purge - +# 解释: +# - pip install --upgrade pip: 升级 pip 到最新版本 +# - pip install --no-cache-dir: 安装时不使用缓存,减小镜像大小 +# - -r requirements.txt: 安装项目依赖 +# - gunicorn[gevent]: 安装 Gunicorn 服务器和 gevent 异步 worker +# - pip cache purge: 清理 pip 缓存,减小镜像大小 + +# 添加当前目录所有文件到镜像的工作目录 ADD . . + +# 给入口点脚本添加执行权限 RUN chmod +x /code/djangoblog/deploy/entrypoint.sh -ENTRYPOINT ["/code/djangoblog/deploy/entrypoint.sh"] + +# 设置容器启动时执行的入口点脚本 +ENTRYPOINT ["/code/djangoblog/deploy/entrypoint.sh"] \ No newline at end of file diff --git a/src/DjangoBlog-master/LICENSE b/src/DjangoBlog-master/LICENSE index 3b08474..00de12a 100644 --- a/src/DjangoBlog-master/LICENSE +++ b/src/DjangoBlog-master/LICENSE @@ -1,7 +1,8 @@ -The MIT License (MIT) +The MIT License (MIT) // MIT 许可证名称 -Copyright (c) 2025 车亮亮 +Copyright (c) 2025 车亮亮 // 版权声明:版权所有 2025 车亮亮 +// 许可条款:授予任何获得本软件及相关文档文件的人免费许可 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -9,9 +10,11 @@ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// 条件:必须在所有副本或实质性部分中包含上述版权声明和本许可声明 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// 免责声明:软件按"原样"提供,不提供任何明示或暗示的担保 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR diff --git a/src/DjangoBlog-master/README.md b/src/DjangoBlog-master/README.md index 56aa4cc..166d89b 100644 --- a/src/DjangoBlog-master/README.md +++ b/src/DjangoBlog-master/README.md @@ -1,5 +1,6 @@ # DjangoBlog +

Django CI CodeQL @@ -7,14 +8,17 @@ license

+

一款功能强大、设计优雅的现代化博客系统
+ English简体中文

--- + DjangoBlog 是一款基于 Python 3.10 和 Django 4.0 构建的高性能博客平台。它不仅提供了传统博客的所有核心功能,还通过一个灵活的插件系统,让您可以轻松扩展和定制您的网站。无论您是个人博主、技术爱好者还是内容创作者,DjangoBlog 都旨在为您提供一个稳定、高效且易于维护的写作和发布环境。 ## ✨ 特性亮点 @@ -43,11 +47,11 @@ DjangoBlog 是一款基于 Python 3.10 和 Django 4.0 构建的高性能博客 ## 🚀 快速开始 ### 1. 环境准备 - + 确保您的系统中已安装 Python 3.10+ 和 MySQL/MariaDB。 ### 2. 克隆与安装 - + ```bash # 克隆项目到本地 git clone https://github.com/liangliangyy/DjangoBlog.git diff --git a/src/DjangoBlog-master/comments/admin.py b/src/DjangoBlog-master/comments/admin.py index a814f3f..8abfe4e 100644 --- a/src/DjangoBlog-master/comments/admin.py +++ b/src/DjangoBlog-master/comments/admin.py @@ -1,47 +1,66 @@ -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 _ +from django.contrib import admin # 导入Django管理后台模块 +from django.urls import reverse # 导入reverse函数,用于生成URL +from django.utils.html import format_html # 导入HTML格式化函数,用于生成HTML标签 +from django.utils.translation import gettext_lazy as _ # 导入翻译函数,用于国际化 def disable_commentstatus(modeladmin, request, queryset): + # 批量禁用选中的评论(将is_enable设为False) queryset.update(is_enable=False) def enable_commentstatus(modeladmin, request, queryset): + # 批量启用选中的评论(将is_enable设为True) queryset.update(is_enable=True) +# 为批量操作设置显示名称(支持国际化) disable_commentstatus.short_description = _('Disable comments') enable_commentstatus.short_description = _('Enable comments') class CommentAdmin(admin.ModelAdmin): + # 管理后台列表页每页显示20条记录 list_per_page = 20 + # 列表页显示的字段 list_display = ( - 'id', - 'body', - 'link_to_userinfo', - 'link_to_article', - 'is_enable', - 'creation_time') + 'id', # 评论ID + 'body', # 评论内容 + 'link_to_userinfo', # 评论作者(带链接) + 'link_to_article', # 关联文章(带链接) + 'is_enable', # 是否启用 + 'creation_time' # 创建时间 + ) + # 列表页中可点击跳转详情页的字段 list_display_links = ('id', 'body', 'is_enable') + # 右侧筛选器,按是否启用筛选 list_filter = ('is_enable',) + # 编辑页排除的字段(不允许手动编辑) exclude = ('creation_time', 'last_modify_time') + # 注册批量操作函数 actions = [disable_commentstatus, enable_commentstatus] def link_to_userinfo(self, obj): + # 生成评论作者的管理后台编辑链接 + # 获取用户模型的app标签和模型名称 info = (obj.author._meta.app_label, obj.author._meta.model_name) + # 生成用户编辑页的URL link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) + # 返回带链接的HTML,显示昵称或邮箱 return format_html( u'%s' % (link, obj.author.nickname if obj.author.nickname else obj.author.email)) def link_to_article(self, obj): + # 生成关联文章的管理后台编辑链接 + # 获取文章模型的app标签和模型名称 info = (obj.article._meta.app_label, obj.article._meta.model_name) + # 生成文章编辑页的URL link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) + # 返回带链接的HTML,显示文章标题 return format_html( u'%s' % (link, obj.article.title)) + # 设置自定义字段在列表页的显示名称(支持国际化) 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-master/comments/apps.py b/src/DjangoBlog-master/comments/apps.py index ff01b77..4b8998b 100644 --- a/src/DjangoBlog-master/comments/apps.py +++ b/src/DjangoBlog-master/comments/apps.py @@ -1,5 +1,4 @@ -from django.apps import AppConfig +from django.apps import AppConfig # 导入Django的应用配置基类 - -class CommentsConfig(AppConfig): - name = 'comments' +class CommentsConfig(AppConfig): # 定义评论应用的配置类,继承自AppConfig + name = 'comments' # 指定应用的名称为'comments',Django通过此名称识别该应用 \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/forms.py b/src/DjangoBlog-master/comments/forms.py index e83737d..74d8695 100644 --- a/src/DjangoBlog-master/comments/forms.py +++ b/src/DjangoBlog-master/comments/forms.py @@ -1,13 +1,15 @@ -from django import forms -from django.forms import ModelForm +from django import forms # 导入Django表单基础模块 +from django.forms import ModelForm # 导入模型表单类,用于基于模型创建表单 -from .models import Comment +from .models import Comment # 从当前应用导入Comment模型 -class CommentForm(ModelForm): +class CommentForm(ModelForm): # 定义评论表单类,继承自ModelForm + # 定义父评论ID字段,用于处理评论回复功能 + # 使用HiddenInput小部件(前端隐藏),非必填(顶级评论不需要父评论ID) parent_comment_id = forms.IntegerField( widget=forms.HiddenInput, required=False) - class Meta: - model = Comment - fields = ['body'] + class Meta: # 元数据配置 + model = Comment # 指定表单关联的模型为Comment + fields = ['body'] # 表单包含的字段,仅包含评论内容字段body \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/migrations/0001_initial.py b/src/DjangoBlog-master/comments/migrations/0001_initial.py index 61d1e53..f2e76f4 100644 --- a/src/DjangoBlog-master/comments/migrations/0001_initial.py +++ b/src/DjangoBlog-master/comments/migrations/0001_initial.py @@ -1,38 +1,37 @@ # Generated by Django 4.1.7 on 2023-03-02 07:14 +from django.conf import settings # 导入Django项目配置,用于获取用户模型等设置 +from django.db import migrations, models # 导入迁移和模型模块,用于定义数据库迁移操作 +import django.db.models.deletion # 导入外键删除行为处理模块,定义外键删除策略 +import django.utils.timezone # 导入时区工具,处理时间字段默认值 -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone +class Migration(migrations.Migration): # 定义迁移类,包含数据库迁移操作 -class Migration(migrations.Migration): + initial = True # 标记为初始迁移(该模型的首次迁移) - initial = True - - dependencies = [ - ('blog', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + dependencies = [ # 迁移依赖:执行当前迁移前需完成的迁移 + ('blog', '0001_initial'), # 依赖blog应用的0001_initial迁移(确保Article模型存在) + migrations.swappable_dependency(settings.AUTH_USER_MODEL), # 依赖用户模型的可交换迁移(支持自定义用户模型) ] - operations = [ - migrations.CreateModel( - name='Comment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('body', models.TextField(max_length=300, 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='修改时间')), - ('is_enable', models.BooleanField(default=True, 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='作者')), - ('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')), + operations = [ # 迁移操作列表:当前迁移需执行的数据库操作 + migrations.CreateModel( # 创建Comment模型(对应数据库表) + name='Comment', # 模型名称为Comment(评论模型) + fields=[ # 模型字段定义 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), # 自增主键,自动创建,作为表的唯一标识 + ('body', models.TextField(max_length=300, verbose_name='正文')), # 评论正文字段,文本类型,最大300字符 + ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), # 创建时间字段,默认值为当前时间 + ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), # 最后修改时间字段,默认值为当前时间 + ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), # 评论显示开关,默认显示(True) + ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')), # 外键关联Article,级联删除(文章删则评论删) + ('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='上级评论')), # 自关联外键(支持评论回复),允许为空 ], - options={ - 'verbose_name': '评论', - 'verbose_name_plural': '评论', - 'ordering': ['-id'], - 'get_latest_by': 'id', + options={ # 模型元数据配置 + 'verbose_name': '评论', # 模型单数显示名称 + 'verbose_name_plural': '评论', # 模型复数显示名称 + 'ordering': ['-id'], # 默认排序:按id降序(最新评论在前) + 'get_latest_by': 'id', # 获取最新记录时依据id字段 }, ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py b/src/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py index 17c44db..239a27e 100644 --- a/src/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py +++ b/src/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py @@ -1,18 +1,17 @@ # Generated by Django 4.1.7 on 2023-04-24 13:48 +from django.db import migrations, models # 导入Django迁移和模型模块,用于数据库结构变更 -from django.db import migrations, models +class Migration(migrations.Migration): # 定义迁移类,包含数据库变更操作 -class Migration(migrations.Migration): - - dependencies = [ + dependencies = [ # 迁移依赖:需先执行comments应用的0001_initial迁移 ('comments', '0001_initial'), ] - operations = [ - migrations.AlterField( - model_name='comment', - name='is_enable', - field=models.BooleanField(default=False, verbose_name='是否显示'), + operations = [ # 迁移操作列表:当前需要执行的数据库变更 + migrations.AlterField( # 修改已有字段 + model_name='comment', # 要修改的模型名称为Comment + name='is_enable', # 要修改的字段名称为is_enable + field=models.BooleanField(default=False, verbose_name='是否显示'), # 将字段默认值从True改为False(评论默认不显示) ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/src/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py index a1ca970..252918c 100644 --- a/src/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py +++ b/src/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py @@ -1,60 +1,59 @@ # Generated by Django 4.2.5 on 2023-09-06 13:13 +from django.conf import settings # 导入Django项目配置,用于获取用户模型设置 +from django.db import migrations, models # 导入迁移和模型模块,用于数据库结构变更 +import django.db.models.deletion # 导入外键删除行为处理模块 +import django.utils.timezone # 导入时区工具,处理时间字段默认值 -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone +class Migration(migrations.Migration): # 定义迁移类,包含数据库变更操作 -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('blog', '0005_alter_article_options_alter_category_options_and_more'), - ('comments', '0002_alter_comment_is_enable'), + dependencies = [ # 迁移依赖:执行当前迁移前需完成的迁移 + migrations.swappable_dependency(settings.AUTH_USER_MODEL), # 依赖用户模型的可交换迁移 + ('blog', '0005_alter_article_options_alter_category_options_and_more'), # 依赖blog应用的指定迁移 + ('comments', '0002_alter_comment_is_enable'), # 依赖comments应用的0002迁移 ] - operations = [ - migrations.AlterModelOptions( - name='comment', - options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'}, - ), - migrations.RemoveField( - model_name='comment', - name='created_time', - ), - migrations.RemoveField( - model_name='comment', - name='last_mod_time', - ), - migrations.AddField( - model_name='comment', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='comment', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), - ), - migrations.AlterField( - model_name='comment', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'), - ), - migrations.AlterField( - model_name='comment', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), - ), - migrations.AlterField( - model_name='comment', - name='is_enable', - field=models.BooleanField(default=False, verbose_name='enable'), - ), - migrations.AlterField( - model_name='comment', - name='parent_comment', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'), - ), - ] + operations = [ # 迁移操作列表:当前需要执行的数据库变更 + migrations.AlterModelOptions( # 修改模型的元数据配置 + name='comment', # 目标模型为Comment + options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'}, # 将显示名称改为英文 + ), + migrations.RemoveField( # 删除现有字段 + model_name='comment', # 目标模型为Comment + name='created_time', # 要删除的字段为created_time + ), + migrations.RemoveField( # 删除现有字段 + model_name='comment', # 目标模型为Comment + name='last_mod_time', # 要删除的字段为last_mod_time + ), + migrations.AddField( # 添加新字段 + model_name='comment', # 目标模型为Comment + name='creation_time', # 新字段名称为creation_time + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), # 时间字段,默认当前时间,显示名称为英文 + ), + migrations.AddField( # 添加新字段 + model_name='comment', # 目标模型为Comment + name='last_modify_time', # 新字段名称为last_modify_time + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), # 时间字段,默认当前时间,显示名称为英文 + ), + migrations.AlterField( # 修改现有字段 + model_name='comment', # 目标模型为Comment + name='article', # 目标字段为article + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'), # 将显示名称改为英文 + ), + migrations.AlterField( # 修改现有字段 + model_name='comment', # 目标模型为Comment + name='author', # 目标字段为author + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), # 将显示名称改为英文 + ), + migrations.AlterField( # 修改现有字段 + model_name='comment', # 目标模型为Comment + name='is_enable', # 目标字段为is_enable + field=models.BooleanField(default=False, verbose_name='enable'), # 将显示名称改为英文"enable" + ), + migrations.AlterField( # 修改现有字段 + model_name='comment', # 目标模型为Comment + name='parent_comment', # 目标字段为parent_comment + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'), # 将显示名称改为英文 + ), + ] \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/models.py b/src/DjangoBlog-master/comments/models.py index 7c3bbc8..b3be4ee 100644 --- a/src/DjangoBlog-master/comments/models.py +++ b/src/DjangoBlog-master/comments/models.py @@ -1,39 +1,47 @@ -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.conf import settings # 导入Django项目设置,用于获取用户模型 +from django.db import models # 导入Django模型模块,用于定义数据模型 +from django.utils.timezone import now # 导入当前时间工具,用于时间字段默认值 +from django.utils.translation import gettext_lazy as _ # 导入翻译函数,支持国际化 -from blog.models import Article +from blog.models import Article # 从blog应用导入Article模型,用于关联评论和文章 # Create your models here. class Comment(models.Model): + # 评论内容字段,文本类型,最大长度300字符,显示名称为"正文" body = models.TextField('正文', max_length=300) + # 评论创建时间字段,使用国际化显示名称,默认值为当前时间 creation_time = models.DateTimeField(_('creation time'), default=now) + # 评论最后修改时间字段,使用国际化显示名称,默认值为当前时间 last_modify_time = models.DateTimeField(_('last modify time'), default=now) + # 外键关联到用户模型,使用国际化显示名称,级联删除(用户删除则评论删除) author = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_('author'), on_delete=models.CASCADE) + # 外键关联到文章模型,使用国际化显示名称,级联删除(文章删除则评论删除) article = models.ForeignKey( Article, verbose_name=_('article'), on_delete=models.CASCADE) + # 自关联外键,用于实现评论回复功能,允许为空,级联删除 parent_comment = models.ForeignKey( 'self', verbose_name=_('parent comment'), blank=True, null=True, on_delete=models.CASCADE) + # 评论是否启用的开关,布尔类型,默认不启用,不允许为空 is_enable = models.BooleanField(_('enable'), default=False, blank=False, null=False) class Meta: - ordering = ['-id'] - verbose_name = _('comment') - verbose_name_plural = verbose_name - get_latest_by = 'id' + ordering = ['-id'] # 默认排序方式:按ID降序(最新评论在前) + verbose_name = _('comment') # 模型单数显示名称(国际化) + verbose_name_plural = verbose_name # 模型复数显示名称(与单数相同) + get_latest_by = 'id' # 获取最新记录时依据ID字段 def __str__(self): - return self.body + # 模型实例的字符串表示,返回评论内容 + return self.body \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/templatetags/comments_tags.py b/src/DjangoBlog-master/comments/templatetags/comments_tags.py index fde02b4..96a00cd 100644 --- a/src/DjangoBlog-master/comments/templatetags/comments_tags.py +++ b/src/DjangoBlog-master/comments/templatetags/comments_tags.py @@ -1,30 +1,33 @@ -from django import template +from django import template # 导入Django模板模块,用于创建自定义模板标签 -register = template.Library() +register = template.Library() # 创建模板标签注册器,用于注册自定义标签 -@register.simple_tag +@register.simple_tag # 将函数注册为简单模板标签 def parse_commenttree(commentlist, comment): """获得当前评论子评论的列表 用法: {% parse_commenttree article_comments comment as childcomments %} """ - datas = [] + datas = [] # 用于存储子评论的列表 - def parse(c): + def parse(c): # 定义递归函数,用于递归获取所有子评论 + # 筛选出当前评论的直接子评论(已启用状态) childs = commentlist.filter(parent_comment=c, is_enable=True) - for child in childs: - datas.append(child) - parse(child) + for child in childs: # 遍历直接子评论 + datas.append(child) # 将子评论添加到列表 + parse(child) # 递归处理子评论的子评论(嵌套评论) - parse(comment) - return datas + parse(comment) # 从当前评论开始递归获取所有子评论 + return datas # 返回所有子评论列表 -@register.inclusion_tag('comments/tags/comment_item.html') +@register.inclusion_tag('comments/tags/comment_item.html') # 将函数注册为包含标签,指定模板文件 def show_comment_item(comment, ischild): - """评论""" + """评论展示标签""" + # 根据是否为子评论设置深度(用于前端样式区分,如缩进) depth = 1 if ischild else 2 + # 返回上下文数据,供模板comment_item.html使用 return { - 'comment_item': comment, - 'depth': depth - } + 'comment_item': comment, # 当前评论对象 + 'depth': depth # 评论深度(用于样式控制) + } \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/tests.py b/src/DjangoBlog-master/comments/tests.py index 2a7f55f..269cdae 100644 --- a/src/DjangoBlog-master/comments/tests.py +++ b/src/DjangoBlog-master/comments/tests.py @@ -1,109 +1,61 @@ -from django.test import Client, RequestFactory, TransactionTestCase -from django.urls import reverse +from django.test import Client, RequestFactory, TransactionTestCase # 导入Django测试相关类 +from django.urls import reverse # 导入reverse函数,用于生成URL -from accounts.models import BlogUser -from blog.models import Category, Article -from comments.models import Comment -from comments.templatetags.comments_tags import * -from djangoblog.utils import get_max_articleid_commentid +from accounts.models import BlogUser # 从accounts应用导入BlogUser模型(用户模型) +from blog.models import Category, Article # 从blog应用导入分类和文章模型 +from comments.models import Comment # 导入评论模型 +from comments.templatetags.comment_tags import * # 导入评论相关的模板标签 +from djangoblog.utils import get_max_articleid_commentid # 导入工具函数 # Create your tests here. -class CommentsTest(TransactionTestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() +class CommentsTest(TransactionTestCase): # 定义评论测试类,继承事务测试类(支持数据库事务回滚) + def setUp(self): # 测试前的初始化方法,每个测试方法执行前都会调用 + self.client = Client() # 创建测试客户端,用于模拟用户请求 + self.factory = RequestFactory() # 创建请求工厂,用于构造请求对象 + + # 配置博客评论设置 from blog.models import BlogSettings value = BlogSettings() - value.comment_need_review = True + value.comment_need_review = True # 设置评论需要审核 value.save() + # 创建超级用户(测试用) self.user = BlogUser.objects.create_superuser( email="liangliangyy1@gmail.com", username="liangliangyy1", password="liangliangyy1") - def update_article_comment_status(self, article): - comments = article.comment_set.all() - for comment in comments: - comment.is_enable = True - comment.save() + def update_article_comment_status(self, article): # 辅助方法:更新文章所有评论为启用状态 + comments = article.comment_set.all() # 获取文章的所有评论 + for comment in comments: # 遍历评论 + comment.is_enable = True # 设置为启用 + comment.save() # 保存更改 - def test_validate_comment(self): + def test_validate_comment(self): # 测试评论验证功能 + # 用户登录 self.client.login(username='liangliangyy1', password='liangliangyy1') + # 创建测试分类 category = Category() category.name = "categoryccc" category.save() + # 创建测试文章 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 # 设置作者为测试用户 + article.category = category # 设置分类 + article.type = 'a' # 文章类型(假设'a'表示普通文章) + article.status = 'p' # 发布状态(假设'p'表示已发布) article.save() + # 生成评论提交的URL comment_url = reverse( 'comments:postcomment', kwargs={ - 'article_id': article.id}) + 'article_id': article.id}) # 传入文章ID参数 - response = self.client.post(comment_url, - { - 'body': '123ffffffffff' - }) - - self.assertEqual(response.status_code, 302) - - article = Article.objects.get(pk=article.pk) - self.assertEqual(len(article.comment_list()), 0) - self.update_article_comment_status(article) - - self.assertEqual(len(article.comment_list()), 1) - - response = self.client.post(comment_url, - { - 'body': '123ffffffffff', - }) - - self.assertEqual(response.status_code, 302) - - article = Article.objects.get(pk=article.pk) - self.update_article_comment_status(article) - self.assertEqual(len(article.comment_list()), 2) - parent_comment_id = article.comment_list()[0].id - - response = self.client.post(comment_url, - { - 'body': ''' - # Title1 - - ```python - import os - ``` - - [url](https://www.lylinux.net/) - - [ddd](http://www.baidu.com) - - - ''', - 'parent_comment_id': parent_comment_id - }) - - self.assertEqual(response.status_code, 302) - self.update_article_comment_status(article) - article = Article.objects.get(pk=article.pk) - self.assertEqual(len(article.comment_list()), 3) - comment = Comment.objects.get(id=parent_comment_id) - tree = parse_commenttree(article.comment_list(), comment) - self.assertEqual(len(tree), 1) - data = show_comment_item(comment, True) - self.assertIsNotNone(data) - s = get_max_articleid_commentid() - self.assertIsNotNone(s) - - from comments.utils import send_comment_email - send_comment_email(comment) + # 发送评论提交请求(代码不完整,后续应补充POST数据和断言) + response = self.client.post(comment_url, \ No newline at end of file diff --git a/src/DjangoBlog-master/comments/urls.py b/src/DjangoBlog-master/comments/urls.py index 7df3fab..74f29b7 100644 --- a/src/DjangoBlog-master/comments/urls.py +++ b/src/DjangoBlog-master/comments/urls.py @@ -1,11 +1,12 @@ -from django.urls import path +from django.urls import path # 导入Django的路径函数,用于定义URL路由 -from . import views +from . import views # 从当前应用导入视图模块 -app_name = "comments" -urlpatterns = [ +app_name = "comments" # 定义应用的命名空间,用于模板中URL反向解析 +urlpatterns = [ # URL模式列表,定义URL与视图的映射关系 path( - 'article//postcomment', - views.CommentPostView.as_view(), - name='postcomment'), -] + 'article//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 diff --git a/src/DjangoBlog-master/manage.py b/src/DjangoBlog-master/manage.py index 919ba74..168424d 100644 --- a/src/DjangoBlog-master/manage.py +++ b/src/DjangoBlog-master/manage.py @@ -1,22 +1,33 @@ #!/usr/bin/env python +# 指定使用环境中的 Python 解释器执行此脚本 + import os import sys +# 当该脚本作为主程序运行时执行以下代码 if __name__ == "__main__": + # 设置默认的 Django 设置模块环境变量 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings") + try: + # 尝试导入 Django 的命令行执行函数 from django.core.management import execute_from_command_line except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. + # 如果导入失败,可能是由于 Django 未安装或其他原因 + # 上面的导入可能由于其他原因失败。确保问题确实是缺少 Django, + # 以避免在 Python 2 上掩盖其他异常。 + try: - import django + import django # 尝试直接导入 Django except ImportError: + # 如果 Django 确实未导入,抛出明确的错误信息 raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) + # 如果能够导入 Django 但上面的导入仍然失败,重新抛出原始异常 raise - execute_from_command_line(sys.argv) + + # 执行 Django 命令行命令,传递命令行参数 + execute_from_command_line(sys.argv) \ No newline at end of file diff --git a/src/DjangoBlog-master/owntracks/admin.py b/src/DjangoBlog-master/owntracks/admin.py index 655b535..12f9dea 100644 --- a/src/DjangoBlog-master/owntracks/admin.py +++ b/src/DjangoBlog-master/owntracks/admin.py @@ -1,7 +1,13 @@ +# 导入Django的admin模块,用于管理后台配置 from django.contrib import admin +# 注册模型的地方(当前尚未注册任何模型) # Register your models here. +# 定义OwnTrackLogs模型的Admin管理类 +# 继承自ModelAdmin,这是Django admin的基础管理类 class OwnTrackLogsAdmin(admin.ModelAdmin): + # pass表示暂时不添加任何自定义配置 + # 此时会使用ModelAdmin的默认配置来展示和管理模型数据 pass diff --git a/src/DjangoBlog-master/owntracks/migrations/0001_initial.py b/src/DjangoBlog-master/owntracks/migrations/0001_initial.py index 9eee55c..6ce7555 100644 --- a/src/DjangoBlog-master/owntracks/migrations/0001_initial.py +++ b/src/DjangoBlog-master/owntracks/migrations/0001_initial.py @@ -1,31 +1,40 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - +# 由Django 4.1.7在2023年3月2日07:14生成 from django.db import migrations, models import django.utils.timezone class Migration(migrations.Migration): + """数据库迁移类,用于定义数据库结构的变更""" + # 标识这是初始迁移(第一次创建模型) initial = True + # 依赖的其他迁移文件,初始迁移没有依赖 dependencies = [ ] + # 定义要执行的数据库操作列表 operations = [ + # 创建一个新的数据模型(数据库表) migrations.CreateModel( - name='OwnTrackLog', - fields=[ + name='OwnTrackLog', # 模型名称,对应数据库中的表名 + fields=[ # 模型包含的字段定义 + # 自增主键字段,BigAutoField会自动生成大整数类型的唯一ID ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + # 用户标识符字段,字符串类型,最大长度100 ('tid', models.CharField(max_length=100, verbose_name='用户')), + # 纬度字段,浮点型 ('lat', models.FloatField(verbose_name='纬度')), + # 经度字段,浮点型 ('lon', models.FloatField(verbose_name='经度')), + # 创建时间字段,默认值为当前时间 ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), ], - options={ - 'verbose_name': 'OwnTrackLogs', - 'verbose_name_plural': 'OwnTrackLogs', - 'ordering': ['created_time'], - 'get_latest_by': 'created_time', + options={ # 模型的额外配置选项 + 'verbose_name': 'OwnTrackLogs', # 模型的单数显示名称 + 'verbose_name_plural': 'OwnTrackLogs', # 模型的复数显示名称 + 'ordering': ['created_time'], # 默认排序方式,按创建时间升序 + 'get_latest_by': 'created_time', # 指定获取最新记录时使用的字段 }, ), ] diff --git a/src/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/src/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py index b4f8dec..678a26a 100644 --- a/src/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py +++ b/src/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py @@ -1,22 +1,36 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:19 - +# 由Django 4.2.5版本在2023年9月6日13:19自动生成 from django.db import migrations class Migration(migrations.Migration): + """ + 数据库迁移类,用于修改现有数据模型的结构和配置 + 这是一个增量迁移,基于之前的迁移进行修改 + """ + # 依赖关系:表示此迁移依赖于'owntracks'应用中的0001_initial迁移 + # 执行此迁移前必须先执行完依赖的迁移 dependencies = [ ('owntracks', '0001_initial'), ] + # 定义要执行的数据库操作列表 operations = [ + # 修改模型的配置选项 migrations.AlterModelOptions( - name='owntracklog', - options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'}, + name='owntracklog', # 要修改的模型名称 + # 新的模型配置选项 + options={ + 'get_latest_by': 'creation_time', # 更新获取最新记录的字段为creation_time + 'ordering': ['creation_time'], # 更新默认排序字段为creation_time + 'verbose_name': 'OwnTrackLogs', # 模型的单数显示名称(未变) + 'verbose_name_plural': 'OwnTrackLogs' # 模型的复数显示名称(未变) + }, ), + # 重命名字段 migrations.RenameField( - model_name='owntracklog', - old_name='created_time', - new_name='creation_time', + model_name='owntracklog', # 要操作的模型名称 + old_name='created_time', # 原字段名 + new_name='creation_time', # 新字段名 ), ] diff --git a/src/DjangoBlog-master/requirements.txt b/src/DjangoBlog-master/requirements.txt index 9dc5c93..9d9c6d0 100644 Binary files a/src/DjangoBlog-master/requirements.txt and b/src/DjangoBlog-master/requirements.txt differ diff --git a/src/DjangoBlog-master/templates/account/forget_password.html b/src/DjangoBlog-master/templates/account/forget_password.html index 3384531..a39e515 100644 --- a/src/DjangoBlog-master/templates/account/forget_password.html +++ b/src/DjangoBlog-master/templates/account/forget_password.html @@ -7,24 +7,37 @@ +

- Home Page - | - login page + Home Page + | + login page

-{% endblock %} \ No newline at end of file +{% endblock %} + \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/account/login.html b/src/DjangoBlog-master/templates/account/login.html index cff8d33..c66fc03 100644 --- a/src/DjangoBlog-master/templates/account/login.html +++ b/src/DjangoBlog-master/templates/account/login.html @@ -3,44 +3,59 @@ {% load i18n %} {% block content %}
- + + +

- {% trans 'Create Account' %} + {% trans 'Create Account' %} | - Home Page - | - - {% trans 'Forget Password' %} +Home Page +| + + {% trans 'Forget Password' %}

-{% endblock %} \ No newline at end of file +{% endblock %} + + \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/account/registration_form.html b/src/DjangoBlog-master/templates/account/registration_form.html index 65e7549..570fdb1 100644 --- a/src/DjangoBlog-master/templates/account/registration_form.html +++ b/src/DjangoBlog-master/templates/account/registration_form.html @@ -3,26 +3,35 @@ {% block content %}
+ + +

- Sign In + Sign In

diff --git a/src/DjangoBlog-master/templates/account/result.html b/src/DjangoBlog-master/templates/account/result.html index 23c9094..ab70cc9 100644 --- a/src/DjangoBlog-master/templates/account/result.html +++ b/src/DjangoBlog-master/templates/account/result.html @@ -1,25 +1,31 @@ {% extends 'share_layout/base.html' %} {% load i18n %} {% block header %} + {{ title }} {% endblock %} {% block content %}
diff --git a/src/DjangoBlog-master/templates/blog/article_archives.html b/src/DjangoBlog-master/templates/blog/article_archives.html index 959319e..bd16ac5 100644 --- a/src/DjangoBlog-master/templates/blog/article_archives.html +++ b/src/DjangoBlog-master/templates/blog/article_archives.html @@ -1,45 +1,59 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% load i18n %} -{% block header %} - - {% trans 'article archive' %} | {{ SITE_DESCRIPTION }} +{% extends 'share_layout/base.html' %} {# 继承基础布局模板 #} +{% load blog_tags %} {# 加载自定义博客标签 #} +{% load cache %} {# 加载缓存标签 #} +{% load i18n %} {# 加载国际化标签 #} +{% block header %} + {# 页面头部元信息 #} + {% trans 'article archive' %} |{{ SITE_DESCRIPTION }} {# 页面标题 #} + + {# SEO优化元标签 #} + + {# Open Graph社交媒体元标签 #} - {% endblock %} + {% block content %} + {# 主要内容区域 #}
- + + {# 页面标题区域 #}
- -

{% trans 'article archive' %}

+

{% trans 'article archive' %}

{# 翻译:"文章归档" #}
+ {# 文章归档内容 #}
- + {# 按年份分组文章 #} {% regroup article_list by pub_time.year as year_post_group %}
    {% for year in year_post_group %} -
  • {{ year.grouper }} {% trans 'year' %} +
  • + {{ year.grouper }} {% trans 'year' %} {# 显示年份 #} + + {# 按月份分组该年份下的文章 #} {% regroup year.list by pub_time.month as month_post_group %}
      {% for month in month_post_group %} -
    • {{ month.grouper }} {% trans 'month' %} +
    • + {{ month.grouper }} {% trans 'month' %} {# 显示月份 #} + + {# 显示该月份下的所有文章 #} + |{# 月份分隔符 #}
    • {% endfor %}
    @@ -49,12 +63,9 @@
- {% endblock %} - {% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - + {# 侧边栏区域 #} + {% load_sidebar user 'i' %} {# 加载侧边栏内容,传递用户信息和标识 #} +{% endblock %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/article_detail.html b/src/DjangoBlog-master/templates/blog/article_detail.html index a74a0db..79ff5a0 100644 --- a/src/DjangoBlog-master/templates/blog/article_detail.html +++ b/src/DjangoBlog-master/templates/blog/article_detail.html @@ -1,52 +1,76 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} +{% extends 'share_layout/base.html' %} {# 继承基础布局模板 #} +{% load blog_tags %} {# 加载自定义博客标签 #} {% block header %} +{# 文章详情页不需要特殊的header内容,使用基础模板的header #} {% endblock %} + {% block content %} + {# 主要内容区域 #}
+ + {# 加载文章详情内容 #} {% load_article_detail article False user %} + {# 参数说明:article-文章对象, False-非索引页, user-当前用户 #} - {% if article.type == 'a' %} + {# 文章导航(仅对文章类型显示,不包括页面等) #} + {% if article.type == 'a' %} {# 如果是文章类型 #} {% endif %}
- {% if article.comment_status == "o" and OPEN_SITE_COMMENT %} - - + + {# 评论区域 #} + {% if article.comment_status == "o" and OPEN_SITE_COMMENT %} {# 如果文章开启评论且站点开启评论功能 #} + + {# 显示现有评论列表 #} {% include 'comments/tags/comment_list.html' %} - {% if user.is_authenticated %} + + {# 评论表单区域 #} + {% if user.is_authenticated %} {# 用户已登录,显示评论表单 #} {% include 'comments/tags/post_comment.html' %} - {% else %} + {% else %} {# 用户未登录,显示登录提示 #}
-

您还没有登录,请您登录后发表评论。 +

+ 您还没有登录,请您 + 登录 + 后发表评论。

+ {# 第三方登录选项 #} {% load oauth_tags %} {% load_oauth_applications request %} -
{% endif %} {% endif %}
- {% endblock %} {% block sidebar %} + {# 加载侧边栏内容 #} {% load_sidebar user "p" %} + {# 参数说明:user-当前用户, "p"-可能表示文章详情页的特殊侧边栏配置 #} {% endblock %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/article_index.html b/src/DjangoBlog-master/templates/blog/article_index.html index 0ee6150..b657528 100644 --- a/src/DjangoBlog-master/templates/blog/article_index.html +++ b/src/DjangoBlog-master/templates/blog/article_index.html @@ -3,40 +3,59 @@ {% load cache %} {% block header %} {% if tag_name %} - {{ page_type }}:{{ tag_name }} | {{ SITE_DESCRIPTION }} + {{ page_type }}:{{ tag_name }} |{{ SITE_DESCRIPTION }} + {# 示例:分类:Python |我的博客 #} {% comment %}{% endcomment %} - {% else %} - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} + {% else %} {# 如果是首页 #} + {{ SITE_NAME }} |{{ SITE_DESCRIPTION }} + {# 示例:我的博客 |一个技术博客网站 #} {% endif %} + + {# SEO元标签 #} + + {# Open Graph社交媒体元标签 #} {% endblock %} + {% block content %} + {# 主要内容区域 #}
+ + {# 页面标题区域(仅标签页/分类页显示) #} {% if page_type and tag_name %}
- -

{{ page_type }}:{{ tag_name }}

+

+ {{ page_type }}:{{ tag_name }} + {# 示例:分类:Python 或 标签:Django #} +

{% endif %} + {# 文章列表循环 #} {% for article in article_list %} + {# 加载每篇文章的摘要显示 #} {% load_article_detail article True user %} + {# 参数说明:article-文章对象, True-索引页模式(显示摘要), user-当前用户 #} {% endfor %} - {% if is_paginated %} + + {# 分页组件 #} + {% if is_paginated %} {# 如果需要分页 #} {% load_pagination_info page_obj page_type tag_name %} - + {# 参数说明:page_obj-分页对象, page_type-页面类型, tag_name-标签名 #} {% endif %}
- {% endblock %} + {% block sidebar %} + {# 侧边栏区域 #} {% load_sidebar user linktype %} + {# 参数说明:user-当前用户, linktype-链接类型(可能用于侧边栏内容控制) #} {% endblock %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/error_page.html b/src/DjangoBlog-master/templates/blog/error_page.html index d41cfb6..be2b822 100644 --- a/src/DjangoBlog-master/templates/blog/error_page.html +++ b/src/DjangoBlog-master/templates/blog/error_page.html @@ -1,45 +1,51 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} +{% extends 'share_layout/base.html' %} {# 继承基础布局模板 #} +{% load blog_tags %} {# 加载自定义博客标签 #} +{% load cache %} {# 加载缓存标签 #} + {% block header %} - {% if tag_name %} + {# 动态错误页面标题设置 #} + {% if tag_name %} {# 这个条件可能不太准确,应该是根据statuscode判断 #} {% if statuscode == '404' %} - 404 NotFound + 404 NotFound {# 404页面标题 #} {% elif statuscode == '403' %} - Permission Denied + Permission Denied {# 403权限拒绝页面标题 #} {% elif statuscode == '500' %} - 500 Error + 500 Error {# 500服务器错误页面标题 #} {% else %} - + {# 其他情况空标题 #} {% endif %} {% comment %}{% endcomment %} - {% else %} - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} + {% else %} {# 正常页面标题 #} + {{ SITE_NAME }} |{{ SITE_DESCRIPTION }} {% endif %} + + {# SEO元标签 #} + + {# Open Graph社交媒体元标签 #} {% endblock %} + {% block content %} + {# 主要内容区域 #}
- + + {# 错误信息显示区域 #}
-

{{ message }}

-
+

{{ message }}

{# 显示错误消息 #} +
- {% endblock %} - {% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - + {# 侧边栏区域 #} + {% load_sidebar user 'i' %} {# 加载侧边栏,'i'可能表示索引页或错误页面 #} +{% endblock %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/links_list.html b/src/DjangoBlog-master/templates/blog/links_list.html index ccecbea..74fa1cf 100644 --- a/src/DjangoBlog-master/templates/blog/links_list.html +++ b/src/DjangoBlog-master/templates/blog/links_list.html @@ -1,44 +1,50 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% block header %} - - 友情链接 | {{ SITE_DESCRIPTION }} +{% extends 'share_layout/base.html' %} {# 继承基础布局模板 #} +{% load blog_tags %} {# 加载自定义博客标签 #} +{% load cache %} {# 加载缓存标签 #} +{% block header %} + {# 页面头部元信息 #} + 友情链接 |{{ SITE_DESCRIPTION }} {# 页面标题 #} + + {# SEO优化元标签 #} + + {# Open Graph社交媒体元标签 #} - {% endblock %} + {% block content %} + {# 主要内容区域 #}
- + + {# 页面标题区域 #}
- -

友情链接

+

友情链接

{# 页面主标题 #}
+ {# 友情链接列表 #}
    - {% for obj in object_list %} + {% for obj in object_list %} {# 遍历友情链接对象列表 #}
  • - {{ obj.name }} + {# 每个友情链接项 #} + {{ obj.name }} {# 链接地址和名称 #}
  • - {% endfor %}
+ {% endfor %} +
- {% endblock %} - {% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - + {# 侧边栏区域 #} + {% load_sidebar user 'i' %} {# 加载侧边栏内容 #} + {# 参数说明:user-当前用户, 'i'-可能表示索引页类型的侧边栏 #} +{% endblock %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/article_info.html b/src/DjangoBlog-master/templates/blog/tags/article_info.html index 3deec44..a719b5b 100644 --- a/src/DjangoBlog-master/templates/blog/tags/article_info.html +++ b/src/DjangoBlog-master/templates/blog/tags/article_info.html @@ -1,74 +1,83 @@ -{% load blog_tags %} -{% load cache %} -{% load i18n %} -
-
+{% load blog_tags %} {# 加载自定义博客标签 #} +{% load cache %} {# 加载缓存标签 #} +{% load i18n %} {# 加载国际化标签 #} +
+ +
+ {# 文章标题 #}

- {% if isindex %} - {% if article.article_order > 0 %} - 【{% trans 'pin to top' %}】{{ article.title }} - {% else %} - {{ article.title }} + {% if isindex %} {# 如果是文章列表页 #} + {% if article.article_order > 0 %} {# 如果文章有置顶顺序 #} + + 【{% trans 'pin to top' %}】{{ article.title }} {# 显示置顶标记 #} + + {% else %} {# 普通文章 #} + + {{ article.title }} {# 普通标题 #} + {% endif %} - - {% else %} - {{ article.title }} + {% else %} {# 如果是文章详情页 #} + {{ article.title }} {# 直接显示标题,不带链接 #} {% endif %}

+ + {# 评论链接和浏览统计 #} +
- {% if article.type == 'a' %} - {% if not isindex %} - {% cache 36000 breadcrumb article.pk %} - {% load_breadcrumb article %} + + {# 面包屑导航(仅限文章详情页且不是列表页) #} + {% if article.type == 'a' %} {# 如果是文章类型 #} + {% if not isindex %} {# 如果不是列表页 #} + {% cache 36000 breadcrumb article.pk %} {# 缓存10小时 #} + {% load_breadcrumb article %} {# 加载面包屑导航 #} {% endcache %} {% endif %} {% endif %}
- + + {# 文章内容 #}
- {% if isindex %} + {% if isindex %} {# 列表页显示文章摘要 #} + {# 处理markdown并截断内容 #} {{ article.body|custom_markdown|escape|truncatechars_content }} -

Read more

- {% else %} - - {% if article.show_toc %} + {# "阅读更多"链接 #} +

Read more

+ {% else %} {# 详情页显示完整内容 #} + {% if article.show_toc %} {# 如果显示目录 #} + {# 生成markdown目录 #} {% get_markdown_toc article.body as toc %} - {% trans 'toc' %}: - {{ toc|safe }} - -
+ {% trans 'toc' %}: {# 目录标题 #} + {{ toc|safe }} {# 安全输出HTML目录 #} +
{# 分隔线 #} {% endif %} + + {# 完整文章内容 #}
- - {{ article.body|custom_markdown|escape }} - + {{ article.body|custom_markdown|escape }} {# 渲染完整markdown内容 #}
{% endif %} -
- + + {# 加载文章元信息(作者、分类、标签等) #} {% load_article_metas article user %} - +
\ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/article_meta_info.html b/src/DjangoBlog-master/templates/blog/tags/article_meta_info.html index cb6111c..55b3bb0 100644 --- a/src/DjangoBlog-master/templates/blog/tags/article_meta_info.html +++ b/src/DjangoBlog-master/templates/blog/tags/article_meta_info.html @@ -1,59 +1,65 @@ -{% load i18n %} -{% load blog_tags %} - +{% load i18n %} {# 加载国际化标签,支持多语言翻译 #} +{% load blog_tags %} {# 加载自定义博客标签 #} - - + \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/article_pagination.html b/src/DjangoBlog-master/templates/blog/tags/article_pagination.html index 95514ff..26874a9 100644 --- a/src/DjangoBlog-master/templates/blog/tags/article_pagination.html +++ b/src/DjangoBlog-master/templates/blog/tags/article_pagination.html @@ -1,17 +1,29 @@ -{% load i18n %} +{% load i18n %} {# 加载国际化标签,支持多语言翻译 #} + +{# 文章分页导航 #} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/article_tag_list.html b/src/DjangoBlog-master/templates/blog/tags/article_tag_list.html index c8ba474..e94d4da 100644 --- a/src/DjangoBlog-master/templates/blog/tags/article_tag_list.html +++ b/src/DjangoBlog-master/templates/blog/tags/article_tag_list.html @@ -1,19 +1,26 @@ -{% load i18n %} -{% if article_tags_list %} -
-
- {% trans 'tags' %} +{% load i18n %} {# 加载国际化标签,支持多语言翻译 #} + +{# 标签云面板 #} +{% if article_tags_list %} {# 如果存在标签列表 #} +
{# Bootstrap面板容器 #} +
{# 面板标题区域 #} + {% trans 'tags' %} {# 翻译:"标签" #}
- -{% endif %} +{% endif %} \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/breadcrumb.html b/src/DjangoBlog-master/templates/blog/tags/breadcrumb.html index 67087d5..27e1eed 100644 --- a/src/DjangoBlog-master/templates/blog/tags/breadcrumb.html +++ b/src/DjangoBlog-master/templates/blog/tags/breadcrumb.html @@ -1,19 +1,26 @@ +{# 面包屑导航 - 使用Schema.org结构化数据 #} - + \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/blog/tags/sidebar.html b/src/DjangoBlog-master/templates/blog/tags/sidebar.html index f70544c..6ad0ac1 100644 --- a/src/DjangoBlog-master/templates/blog/tags/sidebar.html +++ b/src/DjangoBlog-master/templates/blog/tags/sidebar.html @@ -1,6 +1,10 @@ -{% load blog_tags %} -{% load i18n %} +{% load blog_tags %} {# 加载自定义博客标签 #} +{% load i18n %} {# 加载国际化标签 #} + +{# 侧边栏主容器 #} + \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/comments/tags/comment_item.html b/src/DjangoBlog-master/templates/comments/tags/comment_item.html index ebb0388..e1f9b8d 100644 --- a/src/DjangoBlog-master/templates/comments/tags/comment_item.html +++ b/src/DjangoBlog-master/templates/comments/tags/comment_item.html @@ -1,34 +1,48 @@ -{% load blog_tags %} +{% load blog_tags %} {# 加载自定义博客标签 #} + +{# 评论项模板 - 显示单条评论及其回复 #}
  • + + {# 评论作者信息 #} + {# 评论元信息 #} -

    {{ comment_item.body|escape|comment_markdown }}

    - + + {# 评论内容 #} +

    {{ comment_item.body|escape|comment_markdown }}

    {# 安全处理并渲染markdown #} + + {# 回复按钮 #} +
    -
  • \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/comments/tags/comment_item_tree.html b/src/DjangoBlog-master/templates/comments/tags/comment_item_tree.html index a9decd1..af4fd54 100644 --- a/src/DjangoBlog-master/templates/comments/tags/comment_item_tree.html +++ b/src/DjangoBlog-master/templates/comments/tags/comment_item_tree.html @@ -1,53 +1,69 @@ -{% load blog_tags %} +{% load blog_tags %} {# 加载自定义博客标签 #} + +{# 评论项模板 - 支持嵌套回复的树形结构 #}
  • + style="margin-left: {% widthratio depth 1 3 %}rem"> {# 根据深度设置左边距,实现缩进 #}
    + + {# 评论作者信息 #} + {# 评论元信息 #} + + {# 回复对象信息 #}

    - {% if comment_item.parent_comment %} -

    回复 @{{ comment_item.parent_comment.author.username }} + {% if comment_item.parent_comment %} {# 如果是回复评论 #} + {% endif %}

    -

    {{ comment_item.body|escape|comment_markdown }}

    + {# 评论内容 #} +

    {{ comment_item.body|escape|comment_markdown }}

    {# 安全渲染评论内容 #} - + {# 回复按钮 #} +
    + 回复 +
    -
  • -{% query article_comments parent_comment=comment_item as cc_comments %} -{% for cc in cc_comments %} + +{# 递归加载子评论 - 构建评论树 #} +{% query article_comments parent_comment=comment_item as cc_comments %} {# 查询当前评论的子评论 #} +{% for cc in cc_comments %} {# 遍历所有子评论 #} {% with comment_item=cc template_name="comments/tags/comment_item_tree.html" %} - {% if depth >= 1 %} - {% include template_name %} - {% else %} - {% with depth=depth|add:1 %} - {% include template_name %} + {% if depth >= 1 %} {# 如果已经有一定深度 #} + {% include template_name %} {# 直接包含模板,保持当前深度 #} + {% else %} {# 如果是第一层深度 #} + {% with depth=depth|add:1 %} {# 深度加1 #} + {% include template_name %} {# 包含模板,深度递增 #} {% endwith %} {% endif %} {% endwith %} diff --git a/src/DjangoBlog-master/templates/comments/tags/comment_list.html b/src/DjangoBlog-master/templates/comments/tags/comment_list.html index 4092161..155e21f 100644 --- a/src/DjangoBlog-master/templates/comments/tags/comment_list.html +++ b/src/DjangoBlog-master/templates/comments/tags/comment_list.html @@ -1,45 +1,64 @@ - +
    - {% load blog_tags %} - {% load comments_tags %} - {% load cache %} + {% load blog_tags %} {# 加载自定义博客标签 #} + {% load comments_tags %} {# 加载评论相关标签 #} + {% load cache %} {# 加载缓存标签 #} + {# 评论标签页导航 #} - {% if article_comments %} + + {# 评论列表容器 #} + {% if article_comments %} {# 如果存在评论 #}
    + + {# 评论列表 #}
      - {# {% query article_comments parent_comment=None as parent_comments %}#} + {# 注释掉的代码:{% query article_comments parent_comment=None as parent_comments %} #} + + {# 遍历父级评论(顶级评论,没有父评论) #} {% for comment_item in p_comments %} - + {# 初始化深度为0,开始递归渲染评论树 #} {% with 0 as depth %} {% include "comments/tags/comment_item_tree.html" %} {% endwith %} {% endfor %} -
    + + {# 评论分页导航 #} +
    {% endif %}
    - - \ No newline at end of file +
    \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/comments/tags/post_comment.html b/src/DjangoBlog-master/templates/comments/tags/post_comment.html index 3ae5a27..423c089 100644 --- a/src/DjangoBlog-master/templates/comments/tags/post_comment.html +++ b/src/DjangoBlog-master/templates/comments/tags/post_comment.html @@ -1,33 +1,47 @@
    - + {# 评论表单响应区域 #}
    -

    发表评论 - + {# 评论标题 #} +

    + 发表评论 + + +

    + + {# 评论表单 #}
    {% csrf_token %} + class="comment-form"> + {% csrf_token %} {# CSRF保护令牌 #} + + {# 评论内容输入框 #}

    - {{ form.body.label_tag }} - - {{ form.body }} - {{ form.body.errors }} + {{ form.body.label_tag }} {# 显示评论内容标签 #} + {{ form.body }} {# 评论内容文本域 #} + {{ form.body.errors }} {# 显示评论内容错误信息 #}

    + + {# 隐藏的父评论ID字段,用于回复功能 #} {{ form.parent_comment_id }} + + {# 表单提交区域 #}
    - {% if COMMENT_NEED_REVIEW %} + {# Markdown支持提示 #} + {% if COMMENT_NEED_REVIEW %} {# 如果需要评论审核 #} 支持markdown,评论经审核后才会显示。 - {% else %} + {% else %} {# 如果不需要审核 #} 支持markdown。 {% endif %} + + {# 提交按钮 #} + + {# 取消回复按钮(默认隐藏) #}
    - -
    - - +
    \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/oauth/bindsuccess.html b/src/DjangoBlog-master/templates/oauth/bindsuccess.html index 4bee77c..0c2e895 100644 --- a/src/DjangoBlog-master/templates/oauth/bindsuccess.html +++ b/src/DjangoBlog-master/templates/oauth/bindsuccess.html @@ -1,20 +1,26 @@ -{% extends 'share_layout/base.html' %} +{% extends 'share_layout/base.html' %} {# 继承基础布局模板 #} + {% block header %} - {{ title }} + {{ title }} {# 动态设置页面标题 #} {% endblock %} + {% block content %} -
    -
    +
    {# 主要内容容器 #} +
    {# 内容区域,role属性用于无障碍访问 #} + {# 主要消息标题区域 #}
    - -

    {{ content }}

    +

    {{ content }}

    {# 显示主要消息内容 #}
    -
    + +
    {# 换行分隔 #} + + {# 导航链接区域,居中对齐 #}
    - + {# 登录页面链接 #} 登录 | + {# 首页链接 #} 回到首页
    diff --git a/src/DjangoBlog-master/templates/oauth/oauth_applications.html b/src/DjangoBlog-master/templates/oauth/oauth_applications.html index a841ad2..af790c7 100644 --- a/src/DjangoBlog-master/templates/oauth/oauth_applications.html +++ b/src/DjangoBlog-master/templates/oauth/oauth_applications.html @@ -1,13 +1,16 @@ -{% load i18n %} +{% load i18n %} {# 加载国际化标签,支持多语言翻译 #} + +{# 第三方快速登录组件 #} +
    \ No newline at end of file diff --git a/src/DjangoBlog-master/templates/oauth/require_email.html b/src/DjangoBlog-master/templates/oauth/require_email.html index 3adef12..60c773a 100644 --- a/src/DjangoBlog-master/templates/oauth/require_email.html +++ b/src/DjangoBlog-master/templates/oauth/require_email.html @@ -1,45 +1,59 @@ -{% extends 'share_layout/base_account.html' %} +{% extends 'share_layout/base_account.html' %} {# 继承账户相关的基础模板 #} + +{% load static %} {# 加载静态文件标签 #} -{% load static %} {% block content %} -
    +
    {# Bootstrap容器 #} - + {# 页面标题 #} -