diff --git a/src/DjangoBlog-master/blog/__pycache__/__init__.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/__init__.cpython-312.pyc
index fba8897b..ebe8735f 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/__init__.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/__init__.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/admin.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/admin.cpython-312.pyc
index 2d7cc3e6..e9557a1c 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/admin.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/admin.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/apps.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/apps.cpython-312.pyc
index e1565c1c..61058d8e 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/apps.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/apps.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/context_processors.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/context_processors.cpython-312.pyc
index 3e68c2c9..4b91cadf 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/context_processors.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/context_processors.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/documents.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/documents.cpython-312.pyc
index 274bf703..2f08fcf4 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/documents.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/documents.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/middleware.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/middleware.cpython-312.pyc
index 4e2300e7..bbe545de 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/middleware.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/middleware.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/models.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/models.cpython-312.pyc
index cc6b1631..eeb8a3ba 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/models.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/models.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/search_indexes.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/search_indexes.cpython-312.pyc
index 1bb23e31..8d68fc3d 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/search_indexes.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/search_indexes.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/urls.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/urls.cpython-312.pyc
index 11038122..7fe62106 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/urls.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/urls.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/__pycache__/views.cpython-312.pyc b/src/DjangoBlog-master/blog/__pycache__/views.cpython-312.pyc
index c8ff1989..02b25113 100644
Binary files a/src/DjangoBlog-master/blog/__pycache__/views.cpython-312.pyc and b/src/DjangoBlog-master/blog/__pycache__/views.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/admin.py b/src/DjangoBlog-master/blog/admin.py
index 46c34208..a161e216 100644
--- a/src/DjangoBlog-master/blog/admin.py
+++ b/src/DjangoBlog-master/blog/admin.py
@@ -1,3 +1,4 @@
+#sxc 该模块是 Django 博客系统的后台管理配置文件,核心功能是为博客核心模型(Article、Tag、Category 等)注册 Django Admin 管理界面,并通过自定义 Admin 类、表单和批量操作,实现对博客内容的可视化管理,包括文章发布 / 草稿切换、评论状态控制、分类标签管理等功能
from django import forms
from django.contrib import admin
from django.contrib.auth import get_user_model
@@ -16,23 +17,23 @@ class ArticleForm(forms.ModelForm):
model = Article
fields = '__all__'
-
+#sxc 自定义批量操作函数:将选中的文章状态批量更新为 “发布”(status='p')
def makr_article_publish(modeladmin, request, queryset):
queryset.update(status='p')
-
+#sxc 自定义批量操作函数:将选中的文章状态批量更新为 “草稿”(status='d')
def draft_article(modeladmin, request, queryset):
queryset.update(status='d')
-
+#sxc 自定义批量操作函数:将选中的文章评论状态批量更新为 “关闭”(comment_status='c')
def close_article_commentstatus(modeladmin, request, queryset):
queryset.update(comment_status='c')
-
+#sxc 自定义批量操作函数:将选中的文章评论状态批量更新为 “打开”(comment_status='o')
def open_article_commentstatus(modeladmin, request, queryset):
queryset.update(comment_status='o')
-
+#sxc 为批量操作函数设置后台显示名称,支持国际化(_() 函数)
makr_article_publish.short_description = _('Publish selected articles')
draft_article.short_description = _('Draft selected articles')
close_article_commentstatus.short_description = _('Close article comments')
@@ -40,9 +41,11 @@ open_article_commentstatus.short_description = _('Open article comments')
class ArticlelAdmin(admin.ModelAdmin):
+ #sxc 文章模型的 Admin 管理类,自定义后台文章列表和编辑页的显示与功能
list_per_page = 20
search_fields = ('body', 'title')
form = ArticleForm
+ #sxc 列表页显示的字段,涵盖文章核心信息:ID、标题、作者、分类、创建时间、浏览量、状态、类型、排序
list_display = (
'id',
'title',
@@ -58,6 +61,7 @@ class ArticlelAdmin(admin.ModelAdmin):
filter_horizontal = ('tags',)
exclude = ('creation_time', 'last_modify_time')
view_on_site = True
+ #sxc 列表页支持的批量操作,关联前面定义的 4 个批量函数
actions = [
makr_article_publish,
draft_article,
@@ -65,48 +69,69 @@ class ArticlelAdmin(admin.ModelAdmin):
open_article_commentstatus]
def link_to_category(self, obj):
+ #sxc 自定义列表字段:将文章所属分类转为可点击链接,点击跳转到分类的编辑页
+ #sxc 获取分类模型的 APP 标签和模型名,用于生成 Admin 后台的 URL
info = (obj.category._meta.app_label, obj.category._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,))
+ #sxc 生成 HTML 链接,format_html 确保内容安全渲染(避免 XSS)
return format_html(u'%s' % (link, obj.category.name))
link_to_category.short_description = _('category')
def get_form(self, request, obj=None, **kwargs):
+ #sxc 重写表单获取方法:限制 “作者” 字段的可选范围为超级用户
+ #sxc 调用父类方法获取默认表单
form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
+ #sxc 筛选作者下拉框选项,仅显示超级用户(避免普通用户被选为作者)
form.base_fields['author'].queryset = get_user_model(
).objects.filter(is_superuser=True)
return form
def save_model(self, request, obj, form, change):
+ #sxc 重写模型保存方法,可扩展自定义保存逻辑(如自动记录操作日志)
+ #sxc 此处保留默认保存逻辑,如需扩展可在 super 前后添加代码
super(ArticlelAdmin, self).save_model(request, obj, form, change)
def get_view_on_site_url(self, obj=None):
if obj:
+ #sxc 若有文章对象,调用文章模型的 get_full_url 方法生成前台链接
url = obj.get_full_url()
return url
else:
+ #sxc 若无对象(如列表页),返回当前站点的域名
from djangoblog.utils import get_current_site
site = get_current_site().domain
return site
class TagAdmin(admin.ModelAdmin):
+ #sxc 标签模型的 Admin 管理类
+ #sxc 编辑页隐藏自动生成的字段:slug(URL 友好标识)、创建时间、最后修改时间
exclude = ('slug', 'last_mod_time', 'creation_time')
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'parent_category', 'index')
+ #sxc 列表页显示:分类名、父分类、权重排序
+ #sxc 编辑页隐藏自动生成的字段:slug、创建时间、最后修改时间
exclude = ('slug', 'last_mod_time', 'creation_time')
class LinksAdmin(admin.ModelAdmin):
+ #sxc 友情链接模型的 Admin 管理类
+ #sxc 编辑页隐藏自动生成的字段:创建时间、最后修改时间
exclude = ('last_mod_time', 'creation_time')
class SideBarAdmin(admin.ModelAdmin):
+ #sxc 侧边栏模型的 Admin 管理类
+ #sxc 列表页显示:侧边栏标题、内容、是否启用、排序序号
list_display = ('name', 'content', 'is_enable', 'sequence')
+ #sxc 编辑页隐藏自动生成的字段:创建时间、最后修改时间
exclude = ('last_mod_time', 'creation_time')
class BlogSettingsAdmin(admin.ModelAdmin):
+ #sxc 网站配置模型的 Admin 管理类
+ #sxc 未添加额外配置,使用 Django Admin 默认的显示和操作逻辑(适合字段较少的配置类)
pass
diff --git a/src/DjangoBlog-master/blog/apps.py b/src/DjangoBlog-master/blog/apps.py
index 79305878..fbd9a262 100644
--- a/src/DjangoBlog-master/blog/apps.py
+++ b/src/DjangoBlog-master/blog/apps.py
@@ -1,5 +1,8 @@
+#sxc 该模块是 Django 博客应用的配置类模块,用于定义博客应用(blog)的基本配置信息,使 Django 能够识别并正确加载该应用
from django.apps import AppConfig
class BlogConfig(AppConfig):
+ #sxc 博客应用的配置类,继承自 Django 的 AppConfig
+ #sxc 定义应用的名称为 'blog',Django 通过该名称识别并管理该应用
name = 'blog'
diff --git a/src/DjangoBlog-master/blog/context_processors.py b/src/DjangoBlog-master/blog/context_processors.py
index 73e3088b..18f04429 100644
--- a/src/DjangoBlog-master/blog/context_processors.py
+++ b/src/DjangoBlog-master/blog/context_processors.py
@@ -1,3 +1,5 @@
+#sxc 该模块是 Django 博客系统的上下文处理器模块,用于为所有模板提供全局可用的 SEO 相关配置和通用数据,避免在每个视图中重复传递这些数据,提升模板复用性和开发效率
+#sxc 集成缓存机制减少数据库查询,包含网站基本设置、导航数据、评论配置等关键信息
import logging
from django.utils import timezone
@@ -9,35 +11,41 @@ logger = logging.getLogger(__name__)
def seo_processor(requests):
+ #sxc 上下文处理器函数:向所有模板提供全局 SEO 及网站配置数据
+ #sxc 使用缓存减少数据库访问,提升性能,缓存键为'seo_processor'
key = 'seo_processor'
value = cache.get(key)
- if value:
- return value
+ if value:
+ return value #sxc 缓存命中:直接返回缓存的配置数据
else:
logger.info('set processor cache.')
setting = get_blog_setting()
+ #sxc 组装需要传递给模板的全局数据字典
value = {
- 'SITE_NAME': setting.site_name,
- 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,
- 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,
- 'SITE_SEO_DESCRIPTION': setting.site_seo_description,
- 'SITE_DESCRIPTION': setting.site_description,
- 'SITE_KEYWORDS': setting.site_keywords,
+ 'SITE_NAME': setting.site_name, #sxc 网站名称
+ 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,#sxc 是否显示谷歌广告
+ 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,#sxc 谷歌广告代码
+ 'SITE_SEO_DESCRIPTION': setting.site_seo_description,#sxc 网站 SEO 描述(用于搜索引擎)
+ 'SITE_DESCRIPTION': setting.site_description,#sxc 网站描述
+ 'SITE_KEYWORDS': setting.site_keywords,#sxc 网站关键字(用于 SEO)
+ #sxc 网站基础 URL(如https://example.com/)
'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',
- 'ARTICLE_SUB_LENGTH': setting.article_sub_length,
- 'nav_category_list': Category.objects.all(),
+ 'ARTICLE_SUB_LENGTH': setting.article_sub_length,#sxc 文章摘要长度
+ 'nav_category_list': Category.objects.all(),#sxc 导航栏分类列表
+ #sxc 导航栏页面列表(仅包含已发布的页面类型文章)
'nav_pages': Article.objects.filter(
type='p',
status='p'),
- 'OPEN_SITE_COMMENT': setting.open_site_comment,
- 'BEIAN_CODE': setting.beian_code,
- 'ANALYTICS_CODE': setting.analytics_code,
- "BEIAN_CODE_GONGAN": setting.gongan_beiancode,
- "SHOW_GONGAN_CODE": setting.show_gongan_code,
- "CURRENT_YEAR": timezone.now().year,
- "GLOBAL_HEADER": setting.global_header,
- "GLOBAL_FOOTER": setting.global_footer,
- "COMMENT_NEED_REVIEW": setting.comment_need_review,
+ 'OPEN_SITE_COMMENT': setting.open_site_comment,#sxc 是否开启网站评论功能
+ 'BEIAN_CODE': setting.beian_code,#sxc 网站备案号
+ 'ANALYTICS_CODE': setting.analytics_code,#sxc 网站统计代码
+ "BEIAN_CODE_GONGAN": setting.gongan_beiancode,#sxc 公安备案号
+ "SHOW_GONGAN_CODE": setting.show_gongan_code,#sxc 是否显示公安备案号
+ "CURRENT_YEAR": timezone.now().year, #sxc 当前年份(用于页脚版权信息)
+ "GLOBAL_HEADER": setting.global_header,#sxc 公共头部 HTML 代码
+ "GLOBAL_FOOTER": setting.global_footer,#sxc 公共尾部 HTML 代码
+ "COMMENT_NEED_REVIEW": setting.comment_need_review,#sxc 评论是否需要审核
}
+ #sxc 设置缓存,有效期 10 小时(606010 秒)
cache.set(key, value, 60 * 60 * 10)
return value
diff --git a/src/DjangoBlog-master/blog/documents.py b/src/DjangoBlog-master/blog/documents.py
index 0f1db7b7..410f19a1 100644
--- a/src/DjangoBlog-master/blog/documents.py
+++ b/src/DjangoBlog-master/blog/documents.py
@@ -1,15 +1,17 @@
+#sxc 该模块是 Django 博客系统的 Elasticsearch 集成配置模块,核心功能是定义 Elasticsearch 文档模型(性能日志、文章)、实现文档的创建 / 更新 / 删除等管理操作,同时配置 IP 地理信息解析管道,为博客提供高性能的全文搜索和访问性能监控能力
+#sxc 依赖 elasticsearch-dsl 库,支持条件启用(通过 settings.ELASTICSEARCH_DSL 配置),包含文档模型定义、索引管理、数据同步三大核心模块
import time
-
import elasticsearch.client
from django.conf import settings
from elasticsearch_dsl import Document, InnerDoc, Date, Integer, Long, Text, Object, GeoPoint, Keyword, Boolean
from elasticsearch_dsl.connections import connections
from blog.models import Article
-
+#sxc 判断项目是否配置 Elasticsearch,控制后续功能是否启用
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
if ELASTICSEARCH_ENABLED:
+ #sxc 建立与 Elasticsearch 的连接,使用项目配置的地址
connections.create_connection(
hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
from elasticsearch import Elasticsearch
@@ -19,8 +21,10 @@ if ELASTICSEARCH_ENABLED:
c = IngestClient(es)
try:
+ #sxc 检查是否存在 "geoip" 管道(用于解析 IP 地理信息)
c.get_pipeline('geoip')
except elasticsearch.exceptions.NotFoundError:
+ #sxc 若不存在则创建 "geoip" 管道,定义 IP 解析逻辑(从 ip 字段提取地理信息)
c.put_pipeline('geoip', body='''{
"description" : "Add geoip info",
"processors" : [
@@ -34,28 +38,33 @@ if ELASTICSEARCH_ENABLED:
class GeoIp(InnerDoc):
- continent_name = Keyword()
- country_iso_code = Keyword()
- country_name = Keyword()
- location = GeoPoint()
+ #sxc 内部文档类:定义 IP 地理信息的字段结构,用于嵌套在性能日志文档中
+ continent_name = Keyword()#sxc 洲名
+ country_iso_code = Keyword()#sxc 国家 ISO 代码(如 CN、US)
+ country_name = Keyword()#sxc 国家名称
+ location = GeoPoint()#sxc 地理位置坐标(经纬度)
class UserAgentBrowser(InnerDoc):
- Family = Keyword()
- Version = Keyword()
+ #sxc 内部文档类:定义用户代理中的浏览器信息结构
+ Family = Keyword()#sxc 浏览器家族(如 Chrome、Safari)
+ Version = Keyword() #sxc 浏览器版本
class UserAgentOS(UserAgentBrowser):
+ #sxc 内部文档类:定义用户代理中的操作系统信息结构(继承浏览器类字段)
pass
class UserAgentDevice(InnerDoc):
+ #sxc 内部文档类:定义用户代理中的设备信息结构
Family = Keyword()
Brand = Keyword()
Model = Keyword()
class UserAgent(InnerDoc):
+ #sxc 内部文档类:定义完整的用户代理信息结构,嵌套浏览器、OS、设备信息
browser = Object(UserAgentBrowser, required=False)
os = Object(UserAgentOS, required=False)
device = Object(UserAgentDevice, required=False)
@@ -64,6 +73,7 @@ class UserAgent(InnerDoc):
class ElapsedTimeDocument(Document):
+ #sxc Elasticsearch 文档类:定义访问性能日志模型,用于记录页面加载耗时等信息
url = Keyword()
time_taken = Long()
log_datetime = Date()
@@ -72,6 +82,7 @@ class ElapsedTimeDocument(Document):
useragent = Object(UserAgent, required=False)
class Index:
+ #sxc 定义索引配置:索引名为 "performance",分片 1 个、副本 0 个(单节点环境优化)
name = 'performance'
settings = {
"number_of_shards": 1,
@@ -83,8 +94,10 @@ class ElapsedTimeDocument(Document):
class ElaspedTimeDocumentManager:
+ #sxc 性能日志文档管理器:提供索引创建、删除、日志写入等静态方法
@staticmethod
def build_index():
+ #sxc 静态方法:创建 "performance" 索引(若不存在)
from elasticsearch import Elasticsearch
client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
res = client.indices.exists(index="performance")
@@ -93,13 +106,16 @@ class ElaspedTimeDocumentManager:
@staticmethod
def delete_index():
+ #sxc 静态方法:删除 "performance" 索引(忽略 400/404 错误:索引不存在或删除失败)
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
es.indices.delete(index='performance', ignore=[400, 404])
@staticmethod
def create(url, time_taken, log_datetime, useragent, ip):
+ #sxc 静态方法:创建一条性能日志文档,并应用 geoip 管道解析 IP 地理信息
ElaspedTimeDocumentManager.build_index()
+ #sxc 组装用户代理信息(从传入的 useragent 对象提取字段)
ua = UserAgent()
ua.browser = UserAgentBrowser()
ua.browser.Family = useragent.browser.family
@@ -115,7 +131,7 @@ class ElaspedTimeDocumentManager:
ua.device.Model = useragent.device.model
ua.string = useragent.ua_string
ua.is_bot = useragent.is_bot
-
+#sxc 创建性能日志文档:ID 用时间戳(毫秒级)确保唯一,关联 geoip 管道
doc = ElapsedTimeDocument(
meta={
'id': int(
@@ -131,21 +147,26 @@ class ElaspedTimeDocumentManager:
class ArticleDocument(Document):
+ #sxc Elasticsearch 文档类:定义博客文章模型,用于全文搜索
+ #sxc 正文和标题使用 IK 分词器(ik_max_word:细粒度分词;ik_smart:粗粒度搜索)
body = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
title = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
+ #sxc 作者信息(嵌套 Object:包含昵称和 ID,昵称支持分词)
author = Object(properties={
'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
+ #sxc 分类信息(嵌套 Object:包含名称和 ID,名称支持分词)
category = Object(properties={
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
+ #sxc 标签信息(嵌套 Object 列表:包含名称和 ID,名称支持分词)
tags = Object(properties={
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
-
+#sxc 文章其他字段:发布时间、状态、评论状态、类型、浏览量、排序
pub_time = Date()
status = Text()
comment_status = Text()
@@ -154,6 +175,7 @@ class ArticleDocument(Document):
article_order = Integer()
class Index:
+ #sxc 定义索引配置:索引名为 "blog",分片 1 个、副本 0 个(单节点环境优化)
name = 'blog'
settings = {
"number_of_shards": 1,
@@ -163,21 +185,24 @@ class ArticleDocument(Document):
class Meta:
doc_type = 'Article'
-
+#sxc 文章文档管理器:提供索引创建、删除、数据同步(重建 / 更新)等实例方法
class ArticleDocumentManager():
def __init__(self):
self.create_index()
def create_index(self):
+ #sxc 实例方法:初始化 "blog" 索引(根据 ArticleDocument 定义创建)
ArticleDocument.init()
def delete_index(self):
+ #sxc 实例方法:删除 "blog" 索引(忽略 400/404 错误)
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
es.indices.delete(index='blog', ignore=[400, 404])
def convert_to_doc(self, articles):
+ #sxc 实例方法:将 Django Article 模型对象列表转换为 Elasticsearch ArticleDocument 列表
return [
ArticleDocument(
meta={
@@ -202,7 +227,9 @@ class ArticleDocumentManager():
article_order=article.article_order) for article in articles]
def rebuild(self, articles=None):
+ #sxc 实例方法:重建文章索引(全量同步或指定文章同步)
ArticleDocument.init()
+ #sxc 若未指定文章列表,则同步所有 Article 模型数据
articles = articles if articles else Article.objects.all()
docs = self.convert_to_doc(articles)
for doc in docs:
diff --git a/src/DjangoBlog-master/blog/forms.py b/src/DjangoBlog-master/blog/forms.py
index 715be762..350bed6c 100644
--- a/src/DjangoBlog-master/blog/forms.py
+++ b/src/DjangoBlog-master/blog/forms.py
@@ -1,19 +1,25 @@
+#sxc 该模块是 Django 博客系统的搜索表单模块,基于 Haystack 搜索框架扩展自定义搜索表单,用于接收和验证用户的搜索请求,同时记录搜索关键词日志,确保搜索功能的合法性和可追溯性
import logging
-
from django import forms
-from haystack.forms import SearchForm
+from haystack.forms import SearchForm #sxc 导入 Haystack 框架的基础搜索表单类
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #sxc 初始化日志记录器,用于记录搜索相关日志
class BlogSearchForm(SearchForm):
+ #sxc 自定义博客搜索表单类,继承自 Haystack 的 SearchForm,扩展搜索功能
+ #sxc 定义搜索输入字段:querydata(必填,用于接收用户输入的搜索关键词)
querydata = forms.CharField(required=True)
def search(self):
+ #sxc 重写父类 search 方法:执行搜索逻辑,包含表单验证和日志记录
+ #sxc 调用父类 search 方法,获取初始搜索结果集
datas = super(BlogSearchForm, self).search()
+ #sxc 表单验证:若表单数据无效,返回 “无搜索结果” 的默认响应
if not self.is_valid():
return self.no_query_found()
-
+#sxc 日志记录:若存在有效搜索关键词,记录关键词到日志(便于分析用户搜索行为)
if self.cleaned_data['querydata']:
logger.info(self.cleaned_data['querydata'])
+ #sxc 返回搜索结果集(若验证通过且有关键词,返回匹配结果;否则返回空结果)
return datas
diff --git a/src/DjangoBlog-master/blog/management/__pycache__/__init__.cpython-312.pyc b/src/DjangoBlog-master/blog/management/__pycache__/__init__.cpython-312.pyc
index eded7a7c..a7d93081 100644
Binary files a/src/DjangoBlog-master/blog/management/__pycache__/__init__.cpython-312.pyc and b/src/DjangoBlog-master/blog/management/__pycache__/__init__.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/management/commands/build_index.py b/src/DjangoBlog-master/blog/management/commands/build_index.py
index 3c4acd74..263ed495 100644
--- a/src/DjangoBlog-master/blog/management/commands/build_index.py
+++ b/src/DjangoBlog-master/blog/management/commands/build_index.py
@@ -1,3 +1,4 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于在 Elasticsearch 中构建和重建博客系统的搜索索引,支持文章数据和耗时统计数据的索引管理,仅在 Elasticsearch 启用时执行操作。
from django.core.management.base import BaseCommand
from blog.documents import ElapsedTimeDocument, ArticleDocumentManager, ElaspedTimeDocumentManager, \
@@ -6,13 +7,18 @@ from blog.documents import ElapsedTimeDocument, ArticleDocumentManager, ElaspedT
# TODO 参数化
class Command(BaseCommand):
+ #sxc 定义命令的帮助信息,使用python manage.py help build_index可查看
help = 'build search index'
def handle(self, *args, **options):
+ #sxc 命令核心执行逻辑:检查 Elasticsearch 是否启用,若启用则执行索引构建操作
if ELASTICSEARCH_ENABLED:
+ #sxc 构建耗时统计文档的索引(如文章访问耗时等数据)
ElaspedTimeDocumentManager.build_index()
+ #sxc 初始化耗时统计文档的索引结构
manager = ElapsedTimeDocument()
- manager.init()
+ manager.init() #sxc 创建索引映射(mapping)
+ #sxc 处理文章文档的索引:先删除旧索引,再重建新索引
manager = ArticleDocumentManager()
- manager.delete_index()
- manager.rebuild()
+ manager.delete_index() #sxc 删除已存在的文章索引(避免数据冲突)
+ manager.rebuild() #sxc 从数据库同步文章数据并重建索引
diff --git a/src/DjangoBlog-master/blog/management/commands/build_search_words.py b/src/DjangoBlog-master/blog/management/commands/build_search_words.py
index cfe7e0d5..0afe5c93 100644
--- a/src/DjangoBlog-master/blog/management/commands/build_search_words.py
+++ b/src/DjangoBlog-master/blog/management/commands/build_search_words.py
@@ -1,3 +1,4 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于提取博客系统中所有标签(Tag)和分类(Category)的名称,去重后输出为搜索词列表,可用于搜索提示、自动补全等功能的数据源。
from django.core.management.base import BaseCommand
from blog.models import Tag, Category
@@ -5,9 +6,15 @@ from blog.models import Tag, Category
# TODO 参数化
class Command(BaseCommand):
- help = 'build search words'
+ #sxc 定义命令的帮助信息,通过python manage.py help build_search_words可查看
+ help = 'build search words' #sxc 命令功能描述:生成搜索词列表
def handle(self, *args, **options):
+ #sxc 命令核心执行逻辑:提取标签和分类名称,去重后输出
+ #sxc 1. 提取所有标签名称并转换为列表
+ #sxc 2. 提取所有分类名称并转换为列表
+ #sxc 3. 合并两个列表并通过 set 去重,得到唯一的搜索词集合
datas = set([t.name for t in Tag.objects.all()] +
[t.name for t in Category.objects.all()])
+ #sxc 按行输出所有搜索词(每行一个词),便于后续处理或导入
print('\n'.join(datas))
diff --git a/src/DjangoBlog-master/blog/management/commands/clear_cache.py b/src/DjangoBlog-master/blog/management/commands/clear_cache.py
index 0d66172c..ab78b4df 100644
--- a/src/DjangoBlog-master/blog/management/commands/clear_cache.py
+++ b/src/DjangoBlog-master/blog/management/commands/clear_cache.py
@@ -1,11 +1,15 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于清空系统缓存,通过调用缓存工具类的清除方法,快速清理所有缓存数据,适用于缓存数据过期或异常时的手动运维操作。
from django.core.management.base import BaseCommand
-from djangoblog.utils import cache
+from djangoblog.utils import cache #sxc 导入系统缓存工具类
class Command(BaseCommand):
- help = 'clear the whole cache'
+ #sxc 命令帮助信息,执行python manage.py help clear_cache时显示
+ help = 'clear the whole cache' #sxc 命令功能描述:清空所有缓存
def handle(self, *args, **options):
+ #sxc 命令核心执行逻辑:调用缓存工具类的 clear 方法清空所有缓存
cache.clear()
+ #sxc 输出操作成功的提示信息(绿色成功标识)
self.stdout.write(self.style.SUCCESS('Cleared cache\n'))
diff --git a/src/DjangoBlog-master/blog/management/commands/create_testdata.py b/src/DjangoBlog-master/blog/management/commands/create_testdata.py
index 675d2ba6..fe3346be 100644
--- a/src/DjangoBlog-master/blog/management/commands/create_testdata.py
+++ b/src/DjangoBlog-master/blog/management/commands/create_testdata.py
@@ -1,3 +1,4 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于快速生成博客系统所需的测试数据,包括测试用户、分类、标签及文章,适用于开发调试和功能验证场景,生成数据后会自动清理缓存确保数据生效。
from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import make_password
from django.core.management.base import BaseCommand
@@ -6,35 +7,44 @@ from blog.models import Article, Tag, Category
class Command(BaseCommand):
- help = 'create test datas'
+ #sxc 命令帮助信息,执行python manage.py help create_testdata时显示
+ help = 'create test datas' #sxc 命令功能描述:生成测试数据
def handle(self, *args, **options):
+ #sxc 命令核心执行逻辑:创建测试用户、分类、标签和文章数据
+ #sxc 创建或获取测试用户(邮箱:test@test.com,用户名:测试用户,密码:test!q@w#eTYU)
user = get_user_model().objects.get_or_create(
email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0]
-
+#sxc 创建或获取父分类(名称:我是父类目,无父分类)
pcategory = Category.objects.get_or_create(
name='我是父类目', parent_category=None)[0]
-
+#sxc 创建或获取子分类(名称:子类目,父分类为 pcategory)
category = Category.objects.get_or_create(
name='子类目', parent_category=pcategory)[0]
- category.save()
+ category.save() #sxc 保存子分类
+ #sxc 创建基础标签(名称:标签)
basetag = Tag()
basetag.name = "标签"
basetag.save()
+ #sxc 循环创建 19 篇测试文章(标题和内容带序号区分)
for i in range(1, 20):
+ #sxc 创建或获取取文章(关联子分类、测试用户,标题和内容动态生成)
article = Article.objects.get_or_create(
category=category,
- title='nice title ' + str(i),
- body='nice content ' + str(i),
+ title='nice title ' + str(i), #sxc 文章标题:nice title 1~19
+ body='nice content ' + str(i), #sxc 文章内容:nice content 1~19
author=user)[0]
+ #sxc 为每篇文章创建专属标签(名称:标签 1~19)
tag = Tag()
tag.name = "标签" + str(i)
tag.save()
+ #sxc 为文章关联专属标签和基础标签
article.tags.add(tag)
article.tags.add(basetag)
article.save()
-
+#sxc 清理系统缓存,确保新生成的测试数据能被及时加载
from djangoblog.utils import cache
cache.clear()
+ #sxc 输出操作成功的提示信息(绿色成功标识)
self.stdout.write(self.style.SUCCESS('created test datas \n'))
diff --git a/src/DjangoBlog-master/blog/management/commands/ping_baidu.py b/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
index 2c7fbdd6..09167bb0 100644
--- a/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
+++ b/src/DjangoBlog-master/blog/management/commands/ping_baidu.py
@@ -1,50 +1,59 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于向百度搜索引擎推送博客系统的 URL(支持文章、标签、分类及全量 URL),助力搜索引擎快速收录内容,提升博客内容的检索曝光率。
from django.core.management.base import BaseCommand
from djangoblog.spider_notify import SpiderNotify
from djangoblog.utils import get_current_site
from blog.models import Article, Tag, Category
-
+#sxc 获取当前站点的域名(如example.com),用于拼接完整 URL
site = get_current_site().domain
-
class Command(BaseCommand):
- help = 'notify baidu url'
+#sxc 命令帮助信息,执行python manage.py help ping_baidu时显示
+ help = 'notify baidu url' #sxc 命令功能描述:向百度推送 URL
def add_arguments(self, parser):
+ #sxc 为命令添加参数,指定需推送的 URL 类型,限制可选值避免非法输入
parser.add_argument(
- 'data_type',
+ 'data_type', #sxc 参数名:数据类型(需推送的 URL 类别)
type=str,
choices=[
- 'all',
- 'article',
- 'tag',
- 'category'],
- help='article : all article,tag : all tag,category: all category,all: All of these')
+ 'all', #sxc 选项 1:推送所有类型 URL(文章 + 标签 + 分类)
+ 'article',#sxc 选项 2:仅推送文章 URL
+ 'tag',#sxc 选项 3:仅推送标签 URL
+ 'category'], #sxc 选项 4:仅推送分类 URL
+ help='article : all article,tag : all tag,category: all category,all: All of these') #sxc 参数说明
def get_full_url(self, path):
+ #sxc 拼接完整 URL:将相对路径(如 /article/1/)与站点域名组合为绝对 URL
url = "https://{site}{path}".format(site=site, path=path)
return url
def handle(self, *args, **options):
- type = options['data_type']
- self.stdout.write('start get %s' % type)
+ #sxc 命令核心执行逻辑:根据参数筛选 URL → 拼接完整 URL → 推送百度
+ type = options['data_type'] #sxc 获取用户指定的 URL 推送类型
+ self.stdout.write('start get %s' % type) #sxc 输出日志:开始获取指定类型的 URL
- urls = []
+ urls = [] #sxc 存储待推送的完整 URL 列表
+ #sxc 1. 筛选文章 URL(仅状态为 “已发布(p)” 的文章)
if type == 'article' or type == 'all':
for article in Article.objects.filter(status='p'):
- urls.append(article.get_full_url())
+ urls.append(article.get_full_url())#sxc 文章模型自带完整 URL 方法,直接获取
+ #sxc 2. 筛选标签 URL(通过相对路径拼接完整 URL)
if type == 'tag' or type == 'all':
for tag in Tag.objects.all():
- url = tag.get_absolute_url()
- urls.append(self.get_full_url(url))
+ url = tag.get_absolute_url() #sxc 获取标签的相对路径(如 /tag/python/)
+ urls.append(self.get_full_url(url)) #sxc 拼接为完整 URL 并添加到列表
+ #sxc 3. 筛选分类 URL(通过相对路径拼接完整 URL)
if type == 'category' or type == 'all':
for category in Category.objects.all():
- url = category.get_absolute_url()
- urls.append(self.get_full_url(url))
-
+ url = category.get_absolute_url() #sxc 获取分类的相对路径(如 /category/tech/)
+ urls.append(self.get_full_url(url)) #sxc 拼接为完整 URL 并添加到列表
+#sxc 输出日志:展示待推送的 URL 数量
self.stdout.write(
self.style.SUCCESS(
'start notify %d urls' %
len(urls)))
+ #sxc 调用爬虫通知工具类,向百度推送筛选后的 URL 列表
SpiderNotify.baidu_notify(urls)
+ #sxc 输出成功日志:推送完成
self.stdout.write(self.style.SUCCESS('finish notify'))
diff --git a/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py b/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
index d0f46127..f57045c1 100644
--- a/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
+++ b/src/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
@@ -1,3 +1,4 @@
+#sxc 该模块定义了 Django 自定义管理命令,用于同步第三方登录用户(OAuthUser)的头像图片。通过检测头像 URL 有效性,自动更新失效头像,支持从第三方平台重新获取或使用默认头像,确保用户头像展示正常。
import requests
from django.core.management.base import BaseCommand
from django.templatetags.static import static
@@ -8,16 +9,20 @@ from oauth.oauthmanager import get_manager_by_type
class Command(BaseCommand):
- help = 'sync user avatar'
+ #sxc 命令帮助信息,执行python manage.py help sync_user_avatar时显示
+ help = 'sync user avatar' #sxc 命令功能描述:同步用户头像
def test_picture(self, url):
+ #sxc 检测头像 URL 是否有效(状态码 200)
try:
+ #sxc 发送 GET 请求检测 URL,超时时间 2 秒
if requests.get(url, timeout=2).status_code == 200:
return True
except:
- pass
+ pass #sxc 捕获请求异常(如超时、连接失败等),视为无效
def handle(self, *args, **options):
+ #sxc 命令核心执行逻辑:遍历所有第三方用户,检测并同步头像
static_url = static("../")
users = OAuthUser.objects.all()
self.stdout.write(f'开始同步{len(users)}个用户头像')
@@ -25,23 +30,29 @@ class Command(BaseCommand):
self.stdout.write(f'开始同步:{u.nickname}')
url = u.picture
if url:
+ #sxc 判断头像是否为本地静态文件
if url.startswith(static_url):
+ #sxc 检测本地头像 URL 有效性
if self.test_picture(url):
continue
else:
if u.metadata:
+ #sxc 获取对应平台的 OAuth 管理器(如 QQ、GitHub 等)
manage = get_manager_by_type(u.type)
+ #sxc 从元数据中重新获取头像 URL
url = manage.get_picture(u.metadata)
+ #sxc 下载并保存头像,返回本地存储 URL
url = save_user_avatar(url)
- else:
+ else: #sxc 无元数据时使用默认头像
url = static('blog/img/avatar.png')
else:
url = save_user_avatar(url)
else:
url = static('blog/img/avatar.png')
+ #sxc 更新用户头像并保存
if url:
self.stdout.write(
- f'结束同步:{u.nickname}.url:{url}')
+ f'结束同步:{u.nickname}.url:{url}')#sxc 输出同步结果
u.picture = url
- u.save()
- self.stdout.write('结束同步')
+ u.save() #sxc 保存用户头像更新
+ self.stdout.write('结束同步') #sxc 输出同步完成日志
\ No newline at end of file
diff --git a/src/DjangoBlog-master/blog/middleware.py b/src/DjangoBlog-master/blog/middleware.py
index 94dd70c9..17130ffe 100644
--- a/src/DjangoBlog-master/blog/middleware.py
+++ b/src/DjangoBlog-master/blog/middleware.py
@@ -1,42 +1,53 @@
+#sxc 该模块是 Django 博客系统的自定义中间件,核心功能是监控页面渲染耗时、收集访问用户的 IP 和设备信息,并在启用 Elasticsearch 时将这些性能数据写入日志索引,同时支持在页面中替换渲染耗时占位符,实现性能监控和数据统计
import logging
import time
+from ipware import get_client_ip#sxc 导入 IP 获取工具,用于获取访问者真实 IP
+from user_agents import parse #sxc 导入用户代理解析工具,用于识别设备 / 浏览器信息
+from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager #sxc 导入 Elasticsearch 相关配置和管理器
-from ipware import get_client_ip
-from user_agents import parse
-
-from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager
-
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #sxc 初始化日志记录器,用于记录中间件运行中的错误
class OnlineMiddleware(object):
+ #sxc 自定义中间件类:用于监控页面性能、收集访问数据
def __init__(self, get_response=None):
+ #sxc 中间件初始化方法:接收 Django 的 get_response 回调函数,用于传递请求
self.get_response = get_response
super().__init__()
def __call__(self, request):
''' page render time '''
+ #sxc 中间件核心方法:处理请求和响应,计算页面渲染耗时并收集数据,记录请求开始时间(用于计算渲染耗时)
start_time = time.time()
+ #sxc 传递请求到下一个中间件或视图,获取响应对象
response = self.get_response(request)
+ #sxc 提取请求中的用户代理字符串(用于解析设备信息)
http_user_agent = request.META.get('HTTP_USER_AGENT', '')
+ #sxc 获取访问者的 IP 地址(ipware 工具自动处理代理 IP 场景)
ip, _ = get_client_ip(request)
+ #sxc 非流式响应(如 HTML 页面)才处理性能数据(流式响应如文件下载无需处理)
user_agent = parse(http_user_agent)
if not response.streaming:
try:
+ #sxc 计算页面渲染总耗时(当前时间 - 请求开始时间)
cast_time = time.time() - start_time
+ #sxc 若启用 Elasticsearch,将性能数据写入 "performance" 索引
if ELASTICSEARCH_ENABLED:
time_taken = round((cast_time) * 1000, 2)
url = request.path
from django.utils import timezone
+ #sxc 调用管理器创建性能日志文档,包含 URL、耗时、时间、设备、IP 信息
ElaspedTimeDocumentManager.create(
url=url,
time_taken=time_taken,
log_datetime=timezone.now(),
useragent=user_agent,
ip=ip)
+ #sxc 替换页面中的 "" 占位符为实际渲染耗时(保留前 5 位字符)
response.content = response.content.replace(
b'', str.encode(str(cast_time)[:5]))
+ #sxc 捕获处理过程中的异常,记录错误日志(避免中间件报错影响页面正常响应)
except Exception as e:
logger.error("Error OnlineMiddleware: %s" % e)
-
+#sxc 返回处理后的响应对象
return response
diff --git a/src/DjangoBlog-master/blog/migrations/0001_initial.py b/src/DjangoBlog-master/blog/migrations/0001_initial.py
index 3d391b62..003b99b5 100644
--- a/src/DjangoBlog-master/blog/migrations/0001_initial.py
+++ b/src/DjangoBlog-master/blog/migrations/0001_initial.py
@@ -1,3 +1,4 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于定义博客系统的数据模型结构及迁移操作
# Generated by Django 4.1.7 on 2023-03-02 07:14
from django.conf import settings
@@ -8,32 +9,48 @@ import mdeditor.fields
class Migration(migrations.Migration):
-
+#sxc 标记这是初始迁移
initial = True
-
+#sxc 依赖项,依赖于 Django 的用户模型
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
-
+#sxc 迁移操作列表,定义了所有模型的创建操作
operations = [
migrations.CreateModel(
name='BlogSettings',
fields=[
+ #sxc 自增主键 ID
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ #sxc 网站名称字段,默认空字符串,最大长度 200
('sitename', models.CharField(default='', max_length=200, verbose_name='网站名称')),
+ #sxc 网站描述字段,默认空字符串,最大长度 1000
('site_description', models.TextField(default='', max_length=1000, verbose_name='网站描述')),
+ #sxc 网站 SEO 描述字段,用于搜索引擎优化
('site_seo_description', models.TextField(default='', max_length=1000, verbose_name='网站SEO描述')),
+ #sxc 网站关键字字段,用于搜索引擎优化
('site_keywords', models.TextField(default='', max_length=1000, verbose_name='网站关键字')),
+ #sxc 文章摘要长度设置,默认 300
('article_sub_length', models.IntegerField(default=300, verbose_name='文章摘要长度')),
+ #sxc 侧边栏文章数目设置,默认 10
('sidebar_article_count', models.IntegerField(default=10, verbose_name='侧边栏文章数目')),
+ #sxc 侧边栏评论数目设置,默认 5
('sidebar_comment_count', models.IntegerField(default=5, verbose_name='侧边栏评论数目')),
+ #sxc 文章页面默认显示评论数目,默认 5
('article_comment_count', models.IntegerField(default=5, verbose_name='文章页面默认显示评论数目')),
+ #sxc 是否显示谷歌广告的布尔值,默认不显示
('show_google_adsense', models.BooleanField(default=False, verbose_name='是否显示谷歌广告')),
+ #sxc 谷歌广告代码内容,可为空
('google_adsense_codes', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='广告内容')),
+ #sxc 是否打开网站评论功能,默认打开
('open_site_comment', models.BooleanField(default=True, verbose_name='是否打开网站评论功能')),
+ #sxc 网站备案号,可为空
('beiancode', models.CharField(blank=True, default='', max_length=2000, null=True, verbose_name='备案号')),
+ #sxc 网站统计代码
('analyticscode', models.TextField(default='', max_length=1000, verbose_name='网站统计代码')),
+ #sxc 是否显示公安备案号,默认不显示
('show_gongan_code', models.BooleanField(default=False, verbose_name='是否显示公安备案号')),
+ #sxc 公安备案号内容,可为空
('gongan_beiancode', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='公安备案号')),
],
options={
@@ -41,16 +58,24 @@ class Migration(migrations.Migration):
'verbose_name_plural': '网站配置',
},
),
+ #sxc 创建友情链接模型
migrations.CreateModel(
name='Links',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ #sxc 链接名称,唯一
('name', models.CharField(max_length=30, unique=True, verbose_name='链接名称')),
+ #sxc 链接地址,URL 类型
('link', models.URLField(verbose_name='链接地址')),
+ #sxc 排序序号,唯一
('sequence', models.IntegerField(unique=True, verbose_name='排序')),
+ #sxc 是否显示,默认显示
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
+ #sxc 显示类型,可选首页、列表页、文章页面、全站、友情链接页面
('show_type', models.CharField(choices=[('i', '首页'), ('l', '列表页'), ('p', '文章页面'), ('a', '全站'), ('s', '友情链接页面')], default='i', max_length=1, verbose_name='显示类型')),
+ #sxc 创建时间,默认当前时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
+ #sxc 最后修改时间,默认当前时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
],
options={
@@ -59,15 +84,20 @@ class Migration(migrations.Migration):
'ordering': ['sequence'],
},
),
+ #sxc 创建侧边栏模型
migrations.CreateModel(
name='SideBar',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ #sxc 侧边栏标题
('name', models.CharField(max_length=100, verbose_name='标题')),
+ #sxc 侧边栏内容
('content', models.TextField(verbose_name='内容')),
+ #sxc 排序序号,唯一
('sequence', models.IntegerField(unique=True, verbose_name='排序')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否启用')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
+ #sxc 是否启用,默认启用
+ ('is_enable', models.BooleanField(default=True, 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='修改时间')),
],
options={
@@ -76,13 +106,16 @@ class Migration(migrations.Migration):
'ordering': ['sequence'],
},
),
+ #sxc 创建标签模型
migrations.CreateModel(
name='Tag',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ #sxc 标签名称,唯一
('name', models.CharField(max_length=30, unique=True, verbose_name='标签名')),
+ #sxc URL 友好的标识符,默认 'no-slug'
('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
],
options={
@@ -91,15 +124,19 @@ class Migration(migrations.Migration):
'ordering': ['name'],
},
),
+ #sxc 创建分类模型
migrations.CreateModel(
name='Category',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ #sxc 分类名称,唯一
('name', models.CharField(max_length=30, unique=True, verbose_name='分类名')),
('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
+ #sxc 权重排序,数字越大越靠前,默认 0
('index', models.IntegerField(default=0, verbose_name='权重排序-越大越靠前')),
+ #sxc 父级分类,自引用外键,可为空
('parent_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='父级分类')),
],
options={
@@ -108,28 +145,42 @@ class Migration(migrations.Migration):
'ordering': ['-index'],
},
),
+ #sxc 创建文章模型
migrations.CreateModel(
name='Article',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ #sxc 文章标题,唯一
('title', models.CharField(max_length=200, unique=True, verbose_name='标题')),
+ #sxc 文章正文,使用 markdown 编辑器字段
('body', mdeditor.fields.MDTextField(verbose_name='正文')),
+ #sxc 发布时间,默认当前时间
('pub_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='发布时间')),
+ #sxc 文章状态,可选草稿或发表,默认发表
('status', models.CharField(choices=[('d', '草稿'), ('p', '发表')], default='p', max_length=1, verbose_name='文章状态')),
+ #sxc 评论状态,可选打开或关闭,默认打开
('comment_status', models.CharField(choices=[('o', '打开'), ('c', '关闭')], default='o', max_length=1, verbose_name='评论状态')),
+ #sxc 类型,可选文章或页面,默认文章
('type', models.CharField(choices=[('a', '文章'), ('p', '页面')], default='a', max_length=1, verbose_name='类型')),
+ #sxc 浏览量,正整数,默认 0
('views', models.PositiveIntegerField(default=0, verbose_name='浏览量')),
+ #sxc 排序,数字越大越靠前,默认 0
('article_order', models.IntegerField(default=0, verbose_name='排序,数字越大越靠前')),
+ #sxc 是否显示 toc 目录,默认不显示
('show_toc', models.BooleanField(default=False, verbose_name='是否显示toc目录')),
+ #sxc 外键关联到用户模型,作者
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
+ #sxc 外键关联到分类模型
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')),
+ #sxc 多对多关联到标签模型
('tags', models.ManyToManyField(blank=True, to='blog.tag', verbose_name='标签集合')),
],
options={
'verbose_name': '文章',
'verbose_name_plural': '文章',
+ #sxc 先按排序降序,再按发布时间降序
'ordering': ['-article_order', '-pub_time'],
'get_latest_by': 'id',
},
diff --git a/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py b/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
index adbaa36b..3386bfa2 100644
--- a/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
@@ -1,23 +1,28 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于对博客系统的网站配置模型进行扩展,添加公共头部和公共尾部字段
# Generated by Django 4.1.7 on 2023-03-29 06:08
from django.db import migrations, models
class Migration(migrations.Migration):
-
+#sxc 迁移依赖项,依赖于 blog 应用的初始迁移 (0001_initial)
dependencies = [
('blog', '0001_initial'),
]
-
+#sxc 迁移操作列表,定义了为 BlogSettings 模型添加字段的操作
operations = [
+ #sxc 为 BlogSettings 模型添加公共尾部字段
migrations.AddField(
- model_name='blogsettings',
- name='global_footer',
+ model_name='blogsettings',#sxc 为 BlogSettings 模型添加公共尾部字段
+ name='global_footer',#sxc 新增字段名称
+ #sxc 字段类型为文本字段,可为空,默认值为空字符串,verbose_name 为 "公共尾部"
field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'),
),
+ #sxc 为 BlogSettings 模型添加公共头部字段
migrations.AddField(
- model_name='blogsettings',
- name='global_header',
+ model_name='blogsettings',#sxc 要修改的模型名称
+ name='global_header', #sxc 新增字段名称
+ #sxc 字段类型为文本字段,可为空,默认值为空字符串,verbose_name 为 "公共头部"
field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'),
),
]
diff --git a/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py b/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
index e9f55024..177a78ea 100644
--- a/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
+++ b/src/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
@@ -1,17 +1,21 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于在博客系统的网站配置模型中添加评论审核审核设置字段
# Generated by Django 4.2.1 on 2023-05-09 07:45
from django.db import migrations, models
class Migration(migrations.Migration):
+ #sxc 迁移依赖项,依赖于 blog 应用的上一次迁移 (0002_blogsettings_global_footer_and_more)
dependencies = [
('blog', '0002_blogsettings_global_footer_and_more'),
]
-
+#sxc 迁移操作列表,定义了为 BlogSettings 模型添加评论审核字段的操作
operations = [
+ #sxc 为 BlogSettingsSettings 模型添加评论是否需要审核的字段
migrations.AddField(
- model_name='blogsettings',
- name='comment_need_review',
+ model_name='blogsettings', #sxc 要修改的模型名称
+ name='comment_need_review',#sxc 新增字段名称
+ #sxc 字段类型为布尔值,默认值为 False (不需要审核),verbose_name 为 "评论是否需要审核"
field=models.BooleanField(default=False, verbose_name='评论是否需要审核'),
),
]
diff --git a/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
index ceb13982..05df0977 100644
--- a/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
@@ -1,24 +1,29 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于对博客系统的 BlogSettings 模型中的部分字段进行重命名,统一字段命名风格为下划线命名法
# Generated by Django 4.2.1 on 2023-05-09 07:51
from django.db import migrations
class Migration(migrations.Migration):
+ #sxc 迁移依赖项,依赖于 blog 应用的上一次迁移 (0003_blogsettings_comment_need_review)
dependencies = [
('blog', '0003_blogsettings_comment_need_review'),
]
-
+#sxc 迁移操作列表,定义了对 BlogSettings 模型字段的重命名操作
operations = [
+ #sxc 将 analyticscode 字段重命名为 analytics_code
migrations.RenameField(
- model_name='blogsettings',
- old_name='analyticscode',
- new_name='analytics_code',
+ model_name='blogsettings',#sxc 要修改的模型名称
+ old_name='analyticscode',#sxc 原字段名称
+ new_name='analytics_code',#sxc 新字段名称
),
+ #sxc 将 beiancode 字段重命名为 beian_code
migrations.RenameField(
model_name='blogsettings',
old_name='beiancode',
new_name='beian_code',
),
+ #sxc 将 sitename 字段重命名为 site_name
migrations.RenameField(
model_name='blogsettings',
old_name='sitename',
diff --git a/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
index d08e8534..c19c4940 100644
--- a/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
+++ b/src/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
@@ -1,3 +1,4 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于对博客系统的多个模型进行批量修改,包括更新模型选项、字段重命名、字段属性调整以及统一将 verbose_name 从中文改为英文
# Generated by Django 4.2.5 on 2023-09-06 13:13
from django.conf import settings
@@ -8,290 +9,349 @@ import mdeditor.fields
class Migration(migrations.Migration):
-
+#sxc 迁移依赖项,依赖于用户模型和 blog 应用的上一次迁移 (0004_...)
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('blog', '0004_rename_analyticscode_blogsettings_analytics_code_and_more'),
]
-
+#sxc 迁移操作列表,包含模型选项修改、字段移除、新增和属性修改等操作
operations = [
+ #sxc 修改 Article 模型的元选项,将 verbose_name 改为英文
migrations.AlterModelOptions(
name='article',
options={'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', 'verbose_name_plural': 'article'},
),
+ #sxc 修改 Category 模型的元选项,将 verbose_name 改为英文
migrations.AlterModelOptions(
name='category',
options={'ordering': ['-index'], 'verbose_name': 'category', 'verbose_name_plural': 'category'},
),
+ #sxc 修改 Links 模型的元选项,将 verbose_name 改为英文
migrations.AlterModelOptions(
name='links',
options={'ordering': ['sequence'], 'verbose_name': 'link', 'verbose_name_plural': 'link'},
),
+ #sxc 修改 SideBar 模型的元选项,将 verbose_name 改为英文
migrations.AlterModelOptions(
name='sidebar',
options={'ordering': ['sequence'], 'verbose_name': 'sidebar', 'verbose_name_plural': 'sidebar'},
),
+ #sxc 修改 Tag 模型的元选项,将 verbose_name 改为英文
migrations.AlterModelOptions(
name='tag',
options={'ordering': ['name'], 'verbose_name': 'tag', 'verbose_name_plural': 'tag'},
),
+ #sxc 移除 Article 模型中的 created_time 字段
migrations.RemoveField(
model_name='article',
name='created_time',
),
+ #sxc 移除 Article 模型中的 last_mod_time 字段
migrations.RemoveField(
model_name='article',
name='last_mod_time',
),
+ #sxc 移除 Category 模型中的 created_time 字段
migrations.RemoveField(
model_name='category',
name='created_time',
),
+ #sxc 移除 Category 模型中的 last_mod_time 字段
migrations.RemoveField(
model_name='category',
name='last_mod_time',
),
+ #sxc 移除 Links 模型中的 created_time 字段
migrations.RemoveField(
model_name='links',
name='created_time',
),
+ #sxc 移除 SideBar 模型中的 created_time 字段
migrations.RemoveField(
model_name='sidebar',
name='created_time',
),
+ #sxc 移除 Tag 模型中的 created_time 字段
migrations.RemoveField(
model_name='tag',
name='created_time',
),
+ #sxc 移除 Tag 模型中的 last_mod_time 字段
migrations.RemoveField(
model_name='tag',
name='last_mod_time',
),
+ #sxc 为 Article 模型添加 creation_time 字段 (替代原 created_time)
migrations.AddField(
model_name='article',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ #sxc 为 Article 模型添加 last_modify_time 字段 (替代原 last_mod_time)
migrations.AddField(
model_name='article',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
),
+ #sxc 为 Category 模型添加 creation_time 字段 (替代原 created_time)
migrations.AddField(
model_name='category',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ #sxc 为 Category 模型添加 last_modify_time 字段 (替代原 last_mod_time)
migrations.AddField(
model_name='category',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
),
+ #sxc 为 Links 模型添加 creation_time 字段 (替代原 created_time)
migrations.AddField(
model_name='links',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ #sxc 为 SideBar 模型添加 creation_time 字段 (替代原 created_time)
migrations.AddField(
model_name='sidebar',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ #sxc 为 Tag 模型添加 creation_time 字段 (替代原 created_time)
migrations.AddField(
model_name='tag',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ #sxc 为 Tag 模型添加 last_modify_time 字段 (替代原 last_mod_time)
migrations.AddField(
model_name='tag',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
),
+ #sxc 修改 Article 模型的 article_order 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='article_order',
field=models.IntegerField(default=0, verbose_name='order'),
),
+ #sxc 修改 Article 模型的 author 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
),
+ #sxc 修改 Article 模型的 body 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='body',
field=mdeditor.fields.MDTextField(verbose_name='body'),
),
+ #sxc 修改 Article 模型的 category 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='category'),
),
+ #sxc 修改 Article 模型的 comment_status 字段,选项和 verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='comment_status',
field=models.CharField(choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, verbose_name='comment status'),
),
+ #sxc 修改 Article 模型的 pub_time 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='pub_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='publish time'),
),
+ #sxc 修改 Article 模型的 show_toc 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='show_toc',
field=models.BooleanField(default=False, verbose_name='show toc'),
),
+ #sxc 修改 Article 模型的 status 字段,选项和 verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='status',
field=models.CharField(choices=[('d', 'Draft'), ('p', 'Published')], default='p', max_length=1, verbose_name='status'),
),
+ #sxc 修改 Article 模型的 tags 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='tags',
field=models.ManyToManyField(blank=True, to='blog.tag', verbose_name='tag'),
),
+ #sxc 修改 Article 模型的 title 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='title',
field=models.CharField(max_length=200, unique=True, verbose_name='title'),
),
+ #sxc 修改 Article 模型的 type 字段,选项和 verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='type',
field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'),
),
+ #sxc 修改 Article 模型的 views 字段,verbose_name 改为英文
migrations.AlterField(
model_name='article',
name='views',
field=models.PositiveIntegerField(default=0, verbose_name='views'),
),
+ #sxc 修改 BlogSettings 模型的 article_comment_count 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='article_comment_count',
field=models.IntegerField(default=5, verbose_name='article comment count'),
),
+ #sxc 修改 BlogSettings 模型的 article_sub_length 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='article_sub_length',
field=models.IntegerField(default=300, verbose_name='article sub length'),
),
+ #sxc 修改 BlogSettings 模型的 google_adsense_codes 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='google_adsense_codes',
field=models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='adsense code'),
),
+ #sxc 修改 BlogSettings 模型的 open_site_comment 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='open_site_comment',
field=models.BooleanField(default=True, verbose_name='open site comment'),
),
+ #sxc 修改 BlogSettings 模型的 show_google_adsense 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='show_google_adsense',
field=models.BooleanField(default=False, verbose_name='show adsense'),
),
+ #sxc 修改 BlogSettings 模型的 sidebar_article_count 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='sidebar_article_count',
field=models.IntegerField(default=10, verbose_name='sidebar article count'),
),
+ #sxc 修改 BlogSettings 模型的 sidebar_comment_count 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='sidebar_comment_count',
field=models.IntegerField(default=5, verbose_name='sidebar comment count'),
),
+ #sxc 修改 BlogSettings 模型的 site_description 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='site_description',
field=models.TextField(default='', max_length=1000, verbose_name='site description'),
),
+ #sxc 修改 BlogSettings 模型的 site_keywords 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='site_keywords',
field=models.TextField(default='', max_length=1000, verbose_name='site keywords'),
),
+ #sxc 修改 BlogSettings 模型的 site_name 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='site_name',
field=models.CharField(default='', max_length=200, verbose_name='site name'),
),
+ #sxc 修改 BlogSettings 模型的 site_seo_description 字段,verbose_name 改为英文
migrations.AlterField(
model_name='blogsettings',
name='site_seo_description',
field=models.TextField(default='', max_length=1000, verbose_name='site seo description'),
),
+ #sxc 修改 Category 模型的 index 字段,verbose_name 改为英文
migrations.AlterField(
model_name='category',
name='index',
field=models.IntegerField(default=0, verbose_name='index'),
),
+ #sxc 修改 Category 模型的 name 字段,verbose_name 改为英文
migrations.AlterField(
model_name='category',
name='name',
field=models.CharField(max_length=30, unique=True, verbose_name='category name'),
),
+ #sxc 修改 Category 模型的 parent_category 字段,verbose_name 改为英文
migrations.AlterField(
model_name='category',
name='parent_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='parent category'),
),
+ #sxc 修改 Links 模型的 is_enable 字段,verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='is_enable',
field=models.BooleanField(default=True, verbose_name='is show'),
),
+ #sxc 修改 Links 模型的 last_mod_time 字段,verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='last_mod_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
),
+ #sxc 修改 Links 模型的 link 字段,verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='link',
field=models.URLField(verbose_name='link'),
),
+ #sxc 修改 Links 模型的 name 字段,verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='name',
field=models.CharField(max_length=30, unique=True, verbose_name='link name'),
),
+ #sxc 修改 Links 模型的 sequence 字段,verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='sequence',
field=models.IntegerField(unique=True, verbose_name='order'),
),
+ #sxc 修改 Links 模型的 show_type 字段,选项和 verbose_name 改为英文
migrations.AlterField(
model_name='links',
name='show_type',
field=models.CharField(choices=[('i', 'index'), ('l', 'list'), ('p', 'post'), ('a', 'all'), ('s', 'slide')], default='i', max_length=1, verbose_name='show type'),
),
+ #sxc 修改 SideBar 模型的 content 字段,verbose_name 改为英文
migrations.AlterField(
model_name='sidebar',
name='content',
field=models.TextField(verbose_name='content'),
),
+ #sxc 修改 SideBar 模型的 is_enable 字段,verbose_name 改为英文
migrations.AlterField(
model_name='sidebar',
name='is_enable',
field=models.BooleanField(default=True, verbose_name='is enable'),
),
+ #sxc 修改 SideBar 模型的 last_mod_time 字段,verbose_name 改为英文
migrations.AlterField(
model_name='sidebar',
name='last_mod_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
),
+ #sxc 修改 SideBar 模型的 name 字段,verbose_name 改为英文
migrations.AlterField(
model_name='sidebar',
name='name',
field=models.CharField(max_length=100, verbose_name='title'),
),
+ #sxc 修改 SideBar 模型的 sequence 字段,verbose_name 改为英文
migrations.AlterField(
model_name='sidebar',
name='sequence',
field=models.IntegerField(unique=True, verbose_name='order'),
),
+ #sxc 修改 Tag 模型的 name 字段,verbose_name 改为英文
migrations.AlterField(
model_name='tag',
name='name',
diff --git a/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py b/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
index e36feb4c..5faaf0ba 100644
--- a/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
+++ b/src/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
@@ -1,14 +1,15 @@
+#sxc 该模块是 Django 的数据库迁移文件,用于修改博客系统中 BlogSettings 模型的元选项,将其 verbose_name 从中文改为英文
# Generated by Django 4.2.7 on 2024-01-26 02:41
from django.db import migrations
class Migration(migrations.Migration):
-
+#sxc 迁移依赖项,依赖于 blog 应用的上一次迁移 (0005_...)
dependencies = [
('blog', '0005_alter_article_options_alter_category_options_and_more'),
]
-
+#sxc 迁移操作列表,仅包含修改 BlogSettings 模型选项的操作
operations = [
migrations.AlterModelOptions(
name='blogsettings',
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0001_initial.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0001_initial.cpython-312.pyc
index 20ab1a6d..254db7ef 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0001_initial.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0001_initial.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc
index 2f1ec49c..03759f9c 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc
index 4eb3f977..b54392f1 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc
index 7b29e27a..49d24b95 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc
index 31567ae7..8c4c4b90 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc
index f627c051..06a2a1ad 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/migrations/__pycache__/__init__.cpython-312.pyc b/src/DjangoBlog-master/blog/migrations/__pycache__/__init__.cpython-312.pyc
index 8ab3c1a1..ac6227ad 100644
Binary files a/src/DjangoBlog-master/blog/migrations/__pycache__/__init__.cpython-312.pyc and b/src/DjangoBlog-master/blog/migrations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/models.py b/src/DjangoBlog-master/blog/models.py
index 083788bb..b87b5aa6 100644
--- a/src/DjangoBlog-master/blog/models.py
+++ b/src/DjangoBlog-master/blog/models.py
@@ -1,3 +1,4 @@
+#sxc 该模块是 Django 博客系统的核心数据模型定义模块,包含文章、分类、标签、友情链接、侧边栏、网站配置 6 大核心模型,以及基础抽象模型和链接显示类型枚举,同时集成缓存、URL 生成、数据验证等功能,支撑博客的内容管理和系统配置
import logging
import re
from abc import abstractmethod
@@ -8,35 +9,41 @@ from django.db import models
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
-from mdeditor.fields import MDTextField
-from uuslug import slugify
+from mdeditor.fields import MDTextField #sxc 导入 Markdown 编辑器字段,用于文章正文
+from uuslug import slugify #sxc 导入 slug 生成工具,用于生成 URL 友好的标识
-from djangoblog.utils import cache_decorator, cache
-from djangoblog.utils import get_current_site
+from djangoblog.utils import cache_decorator, cache #sxc 导入缓存工具,优化数据查询性能
+from djangoblog.utils import get_current_site #sxc 导入获取当前站点信息的工具
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__) #sxc 初始化日志记录器,用于记录模型操作相关日志
class LinkShowType(models.TextChoices):
- I = ('i', _('index'))
- L = ('l', _('list'))
- P = ('p', _('post'))
- A = ('a', _('all'))
- S = ('s', _('slide'))
+ #sxc 枚举类:定义友情链接的显示位置选项,支持国际化
+ I = ('i', _('index'))#sxc 首页显示
+ L = ('l', _('list'))#sxc 列表页显示
+ P = ('p', _('post'))#sxc 文章页显示
+ A = ('a', _('all'))#sxc 全站显示
+ S = ('s', _('slide'))#sxc 幻灯片区域显示
class BaseModel(models.Model):
- id = models.AutoField(primary_key=True)
+ #sxc 抽象基础模型:为所有业务模型提供通用字段和方法,避免代码
+ id = models.AutoField(primary_key=True) #sxc 自增主键重复
creation_time = models.DateTimeField(_('creation time'), default=now)
last_modify_time = models.DateTimeField(_('modify time'), default=now)
def save(self, *args, **kwargs):
+ #sxc 重写保存方法:优化文章浏览量更新逻辑,自动生成 slug 字段
+ #sxc 判断是否为文章模型的浏览量更新操作(仅更新 views 字段)
is_update_views = isinstance(
self,
Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
if is_update_views:
+ #sxc 浏览量更新:直接通过 QuerySet 更新,避免触发完整 save 流程
Article.objects.filter(pk=self.pk).update(views=self.views)
else:
+ #sxc 非浏览量更新:若模型有 slug 字段,自动生成 URL 友好的 slug(基于 title 或 name 字段)
if 'slug' in self.__dict__:
slug = getattr(
self, 'title') if 'title' in self.__dict__ else getattr(
@@ -55,19 +62,23 @@ class BaseModel(models.Model):
@abstractmethod
def get_absolute_url(self):
+ #sxc 抽象方法:要求子类实现获取自身相对 URL 的逻辑,用于路由跳转
pass
class Article(BaseModel):
"""文章"""
+ #sxc 文章状态选项:草稿(d)、已发布(p)
STATUS_CHOICES = (
('d', _('Draft')),
('p', _('Published')),
)
+ #sxc 评论状态选项:打开(o)、关闭(c)
COMMENT_STATUS = (
('o', _('Open')),
('c', _('Close')),
)
+ #sxc 文章类型选项:文章(a)、独立页面(p,如关于页、联系页)
TYPE = (
('a', _('Article')),
('p', _('Page')),
@@ -109,6 +120,7 @@ class Article(BaseModel):
return self.body
def __str__(self):
+ #sxc 模型实例的字符串表示,返回文章标题
return self.title
class Meta:
@@ -118,6 +130,7 @@ class Article(BaseModel):
get_latest_by = 'id'
def get_absolute_url(self):
+ #sxc 生成文章的相对 URL,用于路由跳转(包含发布日期和文章 ID)
return reverse('blog:detailbyid', kwargs={
'article_id': self.id,
'year': self.creation_time.year,
@@ -129,41 +142,45 @@ class Article(BaseModel):
def get_category_tree(self):
tree = self.category.get_category_tree()
names = list(map(lambda c: (c.name, c.get_absolute_url()), tree))
-
return names
def save(self, *args, **kwargs):
+ #sxc 重写 save 方法:可扩展文章保存时的自定义逻辑(当前仅调用父类方法)
super().save(*args, **kwargs)
def viewed(self):
+ #sxc 文章被浏览时调用,增加浏览量并保存(仅更新 views 字段)
self.views += 1
self.save(update_fields=['views'])
def comment_list(self):
+ #sxc 获取文章的有效评论列表,结果缓存 100 分钟,减少数据库查询
cache_key = 'article_comments_{id}'.format(id=self.id)
value = cache.get(cache_key)
if value:
logger.info('get article comments:{id}'.format(id=self.id))
return value
else:
+ #sxc 查询已启用的评论,按 ID 降序(最新评论在前)
comments = self.comment_set.filter(is_enable=True).order_by('-id')
cache.set(cache_key, comments, 60 * 100)
logger.info('set article comments:{id}'.format(id=self.id))
return comments
def get_admin_url(self):
+ #sxc 生成文章在 Django Admin 后台的编辑 URL,用于快速跳转管理
info = (self._meta.app_label, self._meta.model_name)
return reverse('admin:%s_%s_change' % info, args=(self.pk,))
@cache_decorator(expiration=60 * 100)
def next_article(self):
- # 下一篇
+ #sxc 获取当前文章的下一篇(ID 更大、已发布),结果缓存 100 分钟
return Article.objects.filter(
id__gt=self.id, status='p').order_by('id').first()
@cache_decorator(expiration=60 * 100)
def prev_article(self):
- # 前一篇
+ #sxc 获取当前文章的前一篇(ID 更小、已发布),结果缓存 100 分钟
return Article.objects.filter(id__lt=self.id, status='p').first()
def get_first_image_url(self):
@@ -178,28 +195,31 @@ class Article(BaseModel):
class Category(BaseModel):
+ #sxc 文章分类模型,支持层级结构(父子分类),用于组织文章归属
"""文章分类"""
- name = models.CharField(_('category name'), max_length=30, unique=True)
+ name = models.CharField(_('category name'), max_length=30, unique=True)#sxc 分类名称,唯一不可重复
parent_category = models.ForeignKey(
'self',
verbose_name=_('parent category'),
blank=True,
null=True,
- on_delete=models.CASCADE)
+ on_delete=models.CASCADE)#sxc 父分类,自关联,支持层级;父分类删除时子分类也删除
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
- index = models.IntegerField(default=0, verbose_name=_('index'))
+ index = models.IntegerField(default=0, verbose_name=_('index')) #sxc 排序权重,值越大越靠前
class Meta:
- ordering = ['-index']
+ ordering = ['-index'] #sxc 默认按权重降序排列
verbose_name = _('category')
verbose_name_plural = verbose_name
def get_absolute_url(self):
+ #sxc 生成分类详情页的 URL,基于 slug 参数
return reverse(
'blog:category_detail', kwargs={
'category_name': self.slug})
def __str__(self):
+ #sxc 模型实例的字符串表示,返回分类名称
return self.name
@cache_decorator(60 * 60 * 10)
@@ -211,6 +231,7 @@ class Category(BaseModel):
categorys = []
def parse(category):
+ #sxc 递归函数:将当前分类加入列表,若有父分类则继续解析
categorys.append(category)
if category.parent_category:
parse(category.parent_category)
@@ -225,9 +246,10 @@ class Category(BaseModel):
:return:
"""
categorys = []
- all_categorys = Category.objects.all()
+ all_categorys = Category.objects.all() #sxc 预加载所有分类,减少数据库查询
def parse(category):
+ #sxc 递归函数:将当前分类加入列表,再解析其所有子分类
if category not in categorys:
categorys.append(category)
childs = all_categorys.filter(parent_category=category)
@@ -241,18 +263,22 @@ class Category(BaseModel):
class Tag(BaseModel):
+ #sxc 文章标签模型,用于文章的多维度标记和快速检索
"""文章标签"""
name = models.CharField(_('tag name'), max_length=30, unique=True)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
def __str__(self):
+ #sxc 模型实例的字符串表示,返回标签名称
return self.name
def get_absolute_url(self):
+ #sxc 生成标签详情页的 URL,基于 slug 参数
return reverse('blog:tag_detail', kwargs={'tag_name': self.slug})
@cache_decorator(60 * 60 * 10)
def get_article_count(self):
+ #sxc 获取关联当前标签的文章数量(去重),结果缓存 10 小时
return Article.objects.filter(tags__name=self.name).distinct().count()
class Meta:
@@ -262,6 +288,7 @@ class Tag(BaseModel):
class Links(models.Model):
+ #sxc 友情链接模型,用于管理博客外部链接的展示
"""友情链接"""
name = models.CharField(_('link name'), max_length=30, unique=True)
@@ -278,15 +305,17 @@ class Links(models.Model):
last_mod_time = models.DateTimeField(_('modify time'), default=now)
class Meta:
- ordering = ['sequence']
+ ordering = ['sequence']#sxc 默认按排序序号升序排列
verbose_name = _('link')
verbose_name_plural = verbose_name
def __str__(self):
+ #sxc 模型实例的字符串表示,返回链接名称
return self.name
class SideBar(models.Model):
+ #sxc 侧边栏模型,用于展示自定义 HTML 内容,增强页面灵活性
"""侧边栏,可以展示一些html内容"""
name = models.CharField(_('title'), max_length=100)
content = models.TextField(_('content'))
@@ -367,10 +396,12 @@ class BlogSettings(models.Model):
return self.site_name
def clean(self):
+ # 检查是否已存在其他配置对象,确保配置的唯一性
if BlogSettings.objects.exclude(id=self.id).count():
raise ValidationError(_('There can only be one configuration'))
def save(self, *args, **kwargs):
+ # 调用父类的save方法保存对象
super().save(*args, **kwargs)
from djangoblog.utils import cache
cache.clear()
diff --git a/src/DjangoBlog-master/blog/search_indexes.py b/src/DjangoBlog-master/blog/search_indexes.py
index 7f1dfac1..5e1bc1cd 100644
--- a/src/DjangoBlog-master/blog/search_indexes.py
+++ b/src/DjangoBlog-master/blog/search_indexes.py
@@ -1,13 +1,19 @@
-from haystack import indexes
+#sxc 该模块是 Django 博客系统基于 Haystack 搜索框架的索引配置模块,用于定义文章模型的搜索索引结构,指定搜索字段和索引范围,为博客提供全文搜索功能的基础数据支持
+from haystack import indexes #sxc 导入 Haystack 框架的索引类,用于定义搜索索引
-from blog.models import Article
+from blog.models import Article #sxc 导入博客文章模型,作为索引的数据源
class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
+ #sxc 文章搜索索引类,继承 Haystack 的搜索索引和索引源接口
+ #sxc 定义主搜索字段 text,document=True 表示该字段为主要搜索字段,use_template=True 表示使用模板定义字段内容
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
+ #sxc 实现 Indexable 接口的方法:指定索引关联的模型为 Article
return Article
def index_queryset(self, using=None):
+ #sxc 实现 SearchIndex 的方法:定义需要建立索引的数据集
+ #sxc 仅对状态为 “已发布”(status='p')的文章建立索引,确保搜索结果为公开内容
return self.get_model().objects.filter(status='p')
diff --git a/src/DjangoBlog-master/blog/templatetags/__pycache__/__init__.cpython-312.pyc b/src/DjangoBlog-master/blog/templatetags/__pycache__/__init__.cpython-312.pyc
index bf8b10c6..658272c3 100644
Binary files a/src/DjangoBlog-master/blog/templatetags/__pycache__/__init__.cpython-312.pyc and b/src/DjangoBlog-master/blog/templatetags/__pycache__/__init__.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/templatetags/__pycache__/blog_tags.cpython-312.pyc b/src/DjangoBlog-master/blog/templatetags/__pycache__/blog_tags.cpython-312.pyc
index 6dc320ad..7f6924fc 100644
Binary files a/src/DjangoBlog-master/blog/templatetags/__pycache__/blog_tags.cpython-312.pyc and b/src/DjangoBlog-master/blog/templatetags/__pycache__/blog_tags.cpython-312.pyc differ
diff --git a/src/DjangoBlog-master/blog/templatetags/blog_tags.py b/src/DjangoBlog-master/blog/templatetags/blog_tags.py
index d6cd5d5a..afddf98b 100644
--- a/src/DjangoBlog-master/blog/templatetags/blog_tags.py
+++ b/src/DjangoBlog-master/blog/templatetags/blog_tags.py
@@ -1,3 +1,5 @@
+#sxc 该模块是 Django 博客系统的自定义模板标签库,提供了格式化显示、内容处理、侧边栏加载、面包屑生成等 16 个实用模板标签 / 过滤器,用于在前端模板中复用核心业务逻辑
+#sxc 依赖 Django 模板系统、博客数据模型、缓存工具及第三方头像服务,同时集成插件钩子机制扩展功能
import hashlib
import logging
import random
@@ -21,17 +23,21 @@ from oauth.models import OAuthUser
from djangoblog.plugin_manage import hooks
logger = logging.getLogger(__name__)
-
+#sxc 注册模板标签库,使 Django 模板系统可识别该模块中的标签
register = template.Library()
@register.simple_tag(takes_context=True)
def head_meta(context):
+ #sxc 执行头部元信息的插件过滤钩子,返回处理后的安全 HTML 内容
+ #sxc takes_context=True 表示需要接收模板上下文参数
return mark_safe(hooks.apply_filters('head_meta', '', context))
@register.simple_tag
def timeformat(data):
+ #sxc 简单标签:按项目配置的 TIME_FORMAT 格式化为时间字符串
+ #sxc 异常捕获避免时间格式错误导致页面崩溃,错误时返回空字符串
try:
return data.strftime(settings.TIME_FORMAT)
except Exception as e:
@@ -41,6 +47,8 @@ def timeformat(data):
@register.simple_tag
def datetimeformat(data):
+ #sxc 简单标签:按项目配置的 DATE_TIME_FORMAT 格式化为日期时间字符串
+ #sxc 异常捕获处理,错误时返回空字符串
try:
return data.strftime(settings.DATE_TIME_FORMAT)
except Exception as e:
@@ -51,11 +59,14 @@ def datetimeformat(data):
@register.filter()
@stringfilter
def custom_markdown(content):
+ #sxc 过滤器:将 markdown 格式的内容转换为 HTML,并标记为安全内容(避免 XSS 过滤)
+ #sxc @stringfilter 表示仅接收字符串类型输入
return mark_safe(CommonMarkdown.get_markdown(content))
@register.simple_tag
def get_markdown_toc(content):
+ #sxc 简单标签:获取 markdown 内容的目录(TOC),返回安全 HTML 格式
from djangoblog.utils import CommonMarkdown
body, toc = CommonMarkdown.get_markdown_with_toc(content)
return mark_safe(toc)
@@ -64,6 +75,8 @@ def get_markdown_toc(content):
@register.filter()
@stringfilter
def comment_markdown(content):
+ #sxc 过滤器:处理评论内容的 markdown 转换,先转 HTML 再 sanitize 过滤危险标签
+ #sxc 确保评论内容安全,防止恶意 HTML 注入
content = CommonMarkdown.get_markdown(content)
return mark_safe(sanitize_html(content))
@@ -76,6 +89,8 @@ def truncatechars_content(content):
:param content:
:return:
"""
+ #sxc 过滤器:按博客配置的 “文章摘要长度” 截取内容,保留 HTML 标签结构
+ #sxc is_safe=True 表示返回的 HTML 内容安全,无需额外转义
from django.template.defaultfilters import truncatechars_html
from djangoblog.utils import get_blog_setting
blogsetting = get_blog_setting()
@@ -85,6 +100,7 @@ def truncatechars_content(content):
@register.filter(is_safe=True)
@stringfilter
def truncate(content):
+ #sxc 过滤器:移除 HTML 标签后截取前 150 个字符,用于纯文本摘要场景
from django.utils.html import strip_tags
return strip_tags(content)[:150]
@@ -97,6 +113,8 @@ def load_breadcrumb(article):
:param article:
:return:
"""
+ #sxc 包含标签:加载文章面包屑导航,渲染指定模板并传递数据
+ #sxc 从文章获取分类层级树,反转顺序后拼接网站名称,生成面包屑路径
names = article.get_category_tree()
from djangoblog.utils import get_blog_setting
blogsetting = get_blog_setting()
@@ -105,9 +123,9 @@ def load_breadcrumb(article):
names = names[::-1]
return {
- 'names': names,
- 'title': article.title,
- 'count': len(names) + 1
+ 'names': names,#sxc 面包屑层级列表,每个元素为 (名称,链接)
+ 'title': article.title,#sxc 文章标题
+ 'count': len(names) + 1#sxc 面包屑节点数量
}
@@ -118,6 +136,8 @@ def load_articletags(article):
:param article:
:return:
"""
+ #sxc 包含标签:加载文章的标签列表,渲染标签展示模板
+ #sxc 为每个标签生成链接、文章数量,并随机分配 Bootstrap 颜色
tags = article.tags.all()
tags_list = []
for tag in tags:
@@ -127,7 +147,7 @@ def load_articletags(article):
url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES)
))
return {
- 'article_tags_list': tags_list
+ 'article_tags_list': tags_list#sxc 传递标签数据列表到模板
}
@@ -137,28 +157,38 @@ def load_sidebar(user, linktype):
加载侧边栏
:return:
"""
+ #sxc 包含标签:加载博客侧边栏内容,优先从缓存获取,缓存未命中则查询数据库
+ #sxc 缓存键包含 linktype(链接显示类型),确保不同页面侧边栏内容正确
value = cache.get("sidebar" + linktype)
if value:
- value['user'] = user
+ value['user'] = user#sxc 补充当前用户信息
return value
else:
logger.info('load sidebar')
from djangoblog.utils import get_blog_setting
blogsetting = get_blog_setting()
+ #sxc 查询发布状态的最新文章,数量由博客配置决定
recent_articles = Article.objects.filter(
status='p')[:blogsetting.sidebar_article_count]
+ #sxc 查询所有分类
sidebar_categorys = Category.objects.all()
+ #sxc 查询启用状态的额外侧边栏,按排序号排列
extra_sidebars = SideBar.objects.filter(
is_enable=True).order_by('sequence')
+ #sxc 查询浏览量最高的文章
most_read_articles = Article.objects.filter(status='p').order_by(
'-views')[:blogsetting.sidebar_article_count]
+ #sxc 按月份分组的文章发布日期
dates = Article.objects.datetimes('creation_time', 'month', order='DESC')
+ #sxc 查询启用状态的链接,筛选当前页面类型或全站显示的链接
links = Links.objects.filter(is_enable=True).filter(
Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A))
+ #sxc 查询启用状态的最新评论,数量由博客配置决定
commment_list = Comment.objects.filter(is_enable=True).order_by(
'-id')[:blogsetting.sidebar_comment_count]
# 标签云 计算字体大小
# 根据总数计算出平均值 大小为 (数目/平均值)*步长
+ #sxc 生成标签云:计算标签字体大小(按文章数量比例),随机打乱顺序
increment = 5
tags = Tag.objects.all()
sidebar_tags = None
@@ -185,6 +215,7 @@ def load_sidebar(user, linktype):
'sidebar_tags': sidebar_tags,
'extra_sidebars': extra_sidebars
}
+ #sxc 设置缓存,有效期 3 小时(60603 秒)
cache.set("sidebar" + linktype, value, 60 * 60 * 60 * 3)
logger.info('set sidebar cache.key:{key}'.format(key="sidebar" + linktype))
value['user'] = user
@@ -206,6 +237,7 @@ def load_article_metas(article, user):
@register.inclusion_tag('blog/tags/article_pagination.html')
def load_pagination_info(page_obj, page_type, tag_name):
+ #sxc 包含标签:加载分页信息,根据不同页面类型(首页 / 标签 / 作者 / 分类)生成上下页链接
previous_url = ''
next_url = ''
if page_type == '':
@@ -217,6 +249,7 @@ def load_pagination_info(page_obj, page_type, tag_name):
previous_url = reverse(
'blog:index_page', kwargs={
'page': previous_number})
+ #sxc 标签归档分页逻辑:根据标签 slug 生成链接
if page_type == '分类标签归档':
tag = get_object_or_404(Tag, name=tag_name)
if page_obj.has_next():
@@ -233,6 +266,7 @@ def load_pagination_info(page_obj, page_type, tag_name):
kwargs={
'page': previous_number,
'tag_name': tag.slug})
+ #sxc 作者归档分页逻辑:根据作者名生成链接
if page_type == '作者文章归档':
if page_obj.has_next():
next_number = page_obj.next_page_number()
@@ -248,7 +282,7 @@ def load_pagination_info(page_obj, page_type, tag_name):
kwargs={
'page': previous_number,
'author_name': tag_name})
-
+#sxc 分类归档分页逻辑:根据分类 slug 生成链接
if page_type == '分类目录归档':
category = get_object_or_404(Category, name=tag_name)
if page_obj.has_next():
@@ -281,6 +315,7 @@ def load_article_detail(article, isindex, user):
:param isindex:是否列表页,若是列表页只显示摘要
:return:
"""
+ #sxc 包含标签:加载文章详情内容,根据 isindex 判断是否显示摘要(列表页)或完整内容(详情页)
from djangoblog.utils import get_blog_setting
blogsetting = get_blog_setting()
@@ -297,22 +332,26 @@ def load_article_detail(article, isindex, user):
@register.filter
def gravatar_url(email, size=40):
"""获得gravatar头像"""
+ #sxc 过滤器:获取用户 Gravatar 头像 URL,优先使用 OAuth 用户的自定义头像,其次调用 Gravatar 接口
+ #sxc 缓存头像 URL,减少重复查询
cachekey = 'gravatat/' + email
url = cache.get(cachekey)
if url:
return url
else:
+ #sxc 查询 OAuth 用户表,获取用户自定义头像
usermodels = OAuthUser.objects.filter(email=email)
if usermodels:
o = list(filter(lambda x: x.picture is not None, usermodels))
if o:
return o[0].picture
email = email.encode('utf-8')
-
+#sxc 头像默认图(项目静态文件)
default = static('blog/img/avatar.png')
-
+#sxc 拼接 Gravatar URL,包含 MD5 加密邮箱、默认图、尺寸参数
url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5(
email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
+ #sxc 缓存头像 URL,有效期 10 小时
cache.set(cachekey, url, 60 * 60 * 10)
logger.info('set gravatar cache.key:{key}'.format(key=cachekey))
return url
@@ -321,6 +360,7 @@ def gravatar_url(email, size=40):
@register.filter
def gravatar(email, size=40):
"""获得gravatar头像"""
+ #sxc 过滤器:生成 Gravatar 头像的 img 标签,直接返回可渲染的 HTML
url = gravatar_url(email, size)
return mark_safe(
'
' %
diff --git a/src/DjangoBlog-master/blog/tests.py b/src/DjangoBlog-master/blog/tests.py
index ee135052..aba6cb57 100644
--- a/src/DjangoBlog-master/blog/tests.py
+++ b/src/DjangoBlog-master/blog/tests.py
@@ -1,5 +1,5 @@
+#sxc 该模块基于 Django 框架,通过 TestCase 类对博客系统核心功能进行自动化测试,覆盖文章管理、用户交互、支付流程、第三方集成等场景,确保系统各模块功能正常、接口响应符合预期。
import os
-
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command
@@ -20,11 +20,13 @@ from oauth.models import OAuthUser, OAuthConfig
# Create your tests here.
class ArticleTest(TestCase):
+ #sxc 初始化测试环境,创建客户端(模拟用户请求)和请求工厂(构造自定义请求),为后续测试方法提供基础对象。
def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
+ self.client = Client() #sxc 模拟用户浏览器,用于发送 HTTP 请求
+ self.factory = RequestFactory() #sxc 用于构造自定义请求对象,支持更灵活的请求参数配置
+#sxc 测试文章相关核心功能,包括用户创建、分类 / 标签 / 侧边栏数据初始化、文章增删查、搜索、分页、第三方通知等,覆盖文章生命周期关键流程。
def test_validate_article(self):
+ #sxc 获取当前站点域名,用于后续 URL 相关操作
site = get_current_site().domain
user = BlogUser.objects.get_or_create(
email="liangliangyy@gmail.com",
@@ -33,27 +35,31 @@ class ArticleTest(TestCase):
user.is_staff = True
user.is_superuser = True
user.save()
+ #sxc 测试用户个人主页访问,验证响应状态码为 200(正常访问)
response = self.client.get(user.get_absolute_url())
self.assertEqual(response.status_code, 200)
+ #sxc 测试管理员后台邮件发送日志页面访问(无实际业务校验,仅验证页面可达性)
response = self.client.get('/admin/servermanager/emailsendlog/')
+ #sxc 测试管理员后台操作日志页面访问(无实际业务校验,仅验证页面可达性)
response = self.client.get('admin/admin/logentry/')
+ #sxc 创建测试侧边栏数据,设置排序、名称、内容并启用,用于验证侧边栏功能
s = SideBar()
- s.sequence = 1
- s.name = 'test'
- s.content = 'test content'
- s.is_enable = True
+ s.sequence = 1 #sxc 侧边栏排序序号,决定展示顺序
+ s.name = 'test' #sxc 侧边栏名称
+ s.content = 'test content' #sxc 侧边栏内容
+ s.is_enable = True #sxc 启用侧边栏
s.save()
-
+#sxc 创建测试分类数据,设置名称和时间戳,用于文章分类关联
category = Category()
category.name = "category"
- category.creation_time = timezone.now()
- category.last_mod_time = timezone.now()
+ category.creation_time = timezone.now() #sxc 分类创建时间
+ category.last_mod_time = timezone.now() #sxc 分类最后修改时间
category.save()
-
+#sxc 创建测试标签数据,用于文章标签关联
tag = Tag()
tag.name = "nicetag"
tag.save()
-
+#sxc 创建测试文章,设置标题、内容、作者、分类、类型(a:普通文章)、状态(p:已发布)
article = Article()
article.title = "nicetitle"
article.body = "nicecontent"
@@ -63,15 +69,18 @@ class ArticleTest(TestCase):
article.status = 'p'
article.save()
+ #sxc 验证文章初始状态下标签数量为 0(未关联标签)
self.assertEqual(0, article.tags.count())
+ #sxc 为文章关联标签并重新保存
article.tags.add(tag)
article.save()
+ #sxc 验证文章关联标签后数量为 1(标签关联成功)
self.assertEqual(1, article.tags.count())
-
+#sxc 循环创建 20 篇测试文章,用于后续分页功能测试(模拟多数据场景)
for i in range(20):
article = Article()
- article.title = "nicetitle" + str(i)
- article.body = "nicetitle" + str(i)
+ article.title = "nicetitle" + str(i) #sxc 动态生成文章标题(带序号)
+ article.body = "nicetitle" + str(i) #sxc 动态生成文章内容(带序号)
article.author = user
article.category = category
article.type = 'a'
@@ -79,56 +88,59 @@ class ArticleTest(TestCase):
article.save()
article.tags.add(tag)
article.save()
+ #sxc 检查 Elasticsearch 是否启用,若启用则执行索引构建命令,确保搜索功能可用
from blog.documents import ELASTICSEARCH_ENABLED
if ELASTICSEARCH_ENABLED:
- call_command("build_index")
+ call_command("build_index") #sxc 执行 Django 自定义命令,构建文章搜索索引
response = self.client.get('/search', {'q': 'nicetitle'})
self.assertEqual(response.status_code, 200)
-
+ #sxc 测试搜索功能,搜索关键词 "nicetitle",验证响应状态码为 200
response = self.client.get(article.get_absolute_url())
self.assertEqual(response.status_code, 200)
from djangoblog.spider_notify import SpiderNotify
SpiderNotify.notify(article.get_absolute_url())
response = self.client.get(tag.get_absolute_url())
self.assertEqual(response.status_code, 200)
-
+ #sxc 测试文章详情页访问,验证响应状态码为 200
response = self.client.get(category.get_absolute_url())
self.assertEqual(response.status_code, 200)
-
+#sxc 测试搜索不存在的关键词 “django”,验证搜索页面仍能正常响应(状态码 200)
response = self.client.get('/search', {'q': 'django'})
self.assertEqual(response.status_code, 200)
+ #sxc 测试文章标签模板标签load_articletags,验证返回结果非空
s = load_articletags(article)
self.assertIsNotNone(s)
-
+#sxc 模拟用户登录(使用测试账号),用于后续需登录权限的操作
self.client.login(username='liangliangyy', password='liangliangyy')
-
+#sxc 测试文章归档页面访问,验证响应状态码为 200
response = self.client.get(reverse('blog:archives'))
self.assertEqual(response.status_code, 200)
-
+#sxc 测试所有文章的分页功能,调用check_pagination方法验证分页链接有效性
p = Paginator(Article.objects.all(), settings.PAGINATE_BY)
self.check_pagination(p, '', '')
-
+#sxc 测试指定标签(tag)下文章的分页功能
p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY)
self.check_pagination(p, '分类标签归档', tag.slug)
-
+#sxc 测试指定作者(liangliangyy)下文章的分页功能
p = Paginator(
Article.objects.filter(
author__username='liangliangyy'), settings.PAGINATE_BY)
self.check_pagination(p, '作者文章归档', 'liangliangyy')
-
+#sxc 测试指定分类(category)下文章的分页功能
p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY)
self.check_pagination(p, '分类目录归档', category.slug)
-
+#sxc 测试博客搜索表单BlogSearchForm的search方法,验证表单功能可执行
f = BlogSearchForm()
f.search()
# self.client.login(username='liangliangyy', password='liangliangyy')
+ #sxc 调用百度爬虫通知工具,通知单篇文章的完整 URL
from djangoblog.spider_notify import SpiderNotify
SpiderNotify.baidu_notify([article.get_full_url()])
-
+#sxc 测试头像相关模板标签gravatar_url和gravatar,验证头像 URL 和 HTML 标签生成功能
from blog.templatetags.blog_tags import gravatar_url, gravatar
u = gravatar_url('liangliangyy@gmail.com')
u = gravatar('liangliangyy@gmail.com')
-
+#sxc 创建测试友情链接数据,验证友情链接页面功能
link = Links(
sequence=1,
name="lylinux",
@@ -136,39 +148,47 @@ class ArticleTest(TestCase):
link.save()
response = self.client.get('/links.html')
self.assertEqual(response.status_code, 200)
-
+#sxc 测试 RSS 订阅页面访问,验证响应状态码为 200
response = self.client.get('/feed/')
self.assertEqual(response.status_code, 200)
-
+#sxc 测试站点地图页面访问,验证响应状态码为 200
response = self.client.get('/sitemap.xml')
self.assertEqual(response.status_code, 200)
-
+#sxc 测试管理员删除文章操作,验证页面可达性
self.client.get("/admin/blog/article/1/delete/")
+ #sxc 测试管理员后台邮件日志和操作日志页面访问,验证后台功能稳定性
self.client.get('/admin/servermanager/emailsendlog/')
self.client.get('/admin/admin/logentry/')
self.client.get('/admin/admin/logentry/1/change/')
-
+#sxc 验证分页功能,遍历所有分页页面,确保上一页、下一页链接响应正常
def check_pagination(self, p, type, value):
for page in range(1, p.num_pages + 1):
+ #sxc 调用分页模板标签,获取分页信息
s = load_pagination_info(p.page(page), type, value)
self.assertIsNotNone(s)
+ #sxc 验证上一页链接响应状态
if s['previous_url']:
response = self.client.get(s['previous_url'])
self.assertEqual(response.status_code, 200)
+ #sxc 验证下一页链接响应状态
if s['next_url']:
response = self.client.get(s['next_url'])
self.assertEqual(response.status_code, 200)
-
+#sxc 测试图片上传功能,包括未授权拦截、授权上传、工具函数调用
def test_image(self):
import requests
+ #sxc 下载测试图片(Python logo)用于上传测试
rsp = requests.get(
'https://www.python.org/static/img/python-logo.png')
imagepath = os.path.join(settings.BASE_DIR, 'python.png')
with open(imagepath, 'wb') as file:
file.write(rsp.content)
+ #sxc 测试未授权图片上传,验证返回 403
rsp = self.client.post('/upload')
self.assertEqual(rsp.status_code, 403)
+ #sxc 生成上传授权签名
sign = get_sha256(get_sha256(settings.SECRET_KEY))
+ #sxc 构造授权上传请求,验证上传成功(状态码 200)
with open(imagepath, 'rb') as file:
imgfile = SimpleUploadedFile(
'python.png', file.read(), content_type='image/jpg')
@@ -185,8 +205,9 @@ class ArticleTest(TestCase):
def test_errorpage(self):
rsp = self.client.get('/eee')
self.assertEqual(rsp.status_code, 404)
-
+#sxc 测试 Django 自定义管理命令,覆盖索引构建、爬虫通知、数据生成等运维功能
def test_commands(self):
+ #sxc 创建测试超级用户,用于命令执行权限验证
user = BlogUser.objects.get_or_create(
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
@@ -194,13 +215,13 @@ class ArticleTest(TestCase):
user.is_staff = True
user.is_superuser = True
user.save()
-
+#sxc 创建 QQ 第三方登录配置
c = OAuthConfig()
c.type = 'qq'
c.appkey = 'appkey'
c.appsecret = 'appsecret'
c.save()
-
+#sxc 创建第一个 QQ 第三方用户(关联本地用户
u = OAuthUser()
u.type = 'qq'
u.openid = 'openid'
@@ -211,7 +232,7 @@ class ArticleTest(TestCase):
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
}'''
u.save()
-
+#sxc 创建第二个 QQ 第三方用户(不关联本地用
u = OAuthUser()
u.type = 'qq'
u.openid = 'openid1'
@@ -221,10 +242,11 @@ class ArticleTest(TestCase):
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
}'''
u.save()
-
+#sxc 若 Elasticsearch 启用,执行索引构建命令
from blog.documents import ELASTICSEARCH_ENABLED
if ELASTICSEARCH_ENABLED:
call_command("build_index")
+ #sxc 执行百度爬虫通知、测试数据生成、缓存清理、头像同步、搜索词构建等命令
call_command("ping_baidu", "all")
call_command("create_testdata")
call_command("clear_cache")
diff --git a/src/DjangoBlog-master/blog/urls.py b/src/DjangoBlog-master/blog/urls.py
index adf27036..9a67ba1a 100644
--- a/src/DjangoBlog-master/blog/urls.py
+++ b/src/DjangoBlog-master/blog/urls.py
@@ -1,3 +1,4 @@
+#sxc 该模块是 Django 博客应用的 URL 路由配置模块,定义了博客前台所有页面的访问路径与对应视图的映射关系,包括首页、文章详情页、分类页、标签页等核心页面,同时配置了缓存和分页路由,实现 URL 的规范化管理和页面访问控制
from django.urls import path
from django.views.decorators.cache import cache_page
@@ -5,56 +6,69 @@ from . import views
app_name = "blog"
urlpatterns = [
+ #sxc 首页路由:映射到 IndexView 视图,名称为 'index'
path(
r'',
views.IndexView.as_view(),
name='index'),
+ #sxc 首页分页路由:接收页码参数,名称为 'index_page'
path(
r'page//',
views.IndexView.as_view(),
name='index_page'),
+ #sxc 文章详情页路由:通过年月日和文章 ID 访问,名称为 'detailbyid'
path(
r'article////.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),
+ #sxc 分类详情页路由:通过分类 slug 访问,名称为 'category_detail'
path(
r'category/.html',
views.CategoryDetailView.as_view(),
name='category_detail'),
+ #sxc 分类详情分页路由:包含分类 slug 和页码,名称为 'category_detail_page'
path(
r'category//.html',
views.CategoryDetailView.as_view(),
name='category_detail_page'),
+ #sxc 作者文章页路由:通过作者名访问,名称为 'author_detail'
path(
r'author/.html',
views.AuthorDetailView.as_view(),
name='author_detail'),
+ #sxc 作者文章分页路由:包含作者名和页码,名称为 'author_detail_page'
path(
r'author//.html',
views.AuthorDetailView.as_view(),
name='author_detail_page'),
+ #sxc 标签详情页路由:通过标签 slug 访问,名称为 'tag_detail'
path(
r'tag/.html',
views.TagDetailView.as_view(),
name='tag_detail'),
+ #sxc 标签详情分页路由:包含标签 slug 和页码,名称为 'tag_detail_page'
path(
r'tag//.html',
views.TagDetailView.as_view(),
name='tag_detail_page'),
+ #sxc 归档页面路由:使用缓存装饰器,缓存 1 小时 (60*60 秒),名称为 'archives'
path(
'archives.html',
cache_page(
60 * 60)(
views.ArchivesView.as_view()),
name='archives'),
+ #sxc 友情链接页面路由:映射到 LinkListView 视图,名称为 'links'
path(
'links.html',
views.LinkListView.as_view(),
name='links'),
+ #sxc 文件上传路由:用于处理文件上传请求,名称为 'upload'
path(
r'upload',
views.fileupload,
name='upload'),
+ #sxc 缓存清理路由:用于触发缓存清理操作,名称为 'clean'
path(
r'clean',
views.clean_cache_view,
diff --git a/src/DjangoBlog-master/blog/views.py b/src/DjangoBlog-master/blog/views.py
index d5dc7ec0..18ef53c5 100644
--- a/src/DjangoBlog-master/blog/views.py
+++ b/src/DjangoBlog-master/blog/views.py
@@ -1,3 +1,4 @@
+#sxc 该模块基于 Django 框架,实现博客系统的各类视图功能,包括文章列表、详情、分类、标签、搜索、文件上传、错误页处理等,同时集成缓存、插件钩子等机制,保障页面渲染和业务逻辑的稳定性与可扩展性。
import logging
import os
import uuid
@@ -26,6 +27,7 @@ logger = logging.getLogger(__name__)
class ArticleListView(ListView):
# template_name属性用于指定使用哪个模板进行渲染
+ #sxc 文章列表视图基类,封装分页、缓存、上下文处理等通用逻辑,为子类(首页、分类、标签等列表)提供基础支持
template_name = 'blog/article_index.html'
# context_object_name属性用于给上下文变量取名(在模板中使用该名字)
@@ -42,6 +44,7 @@ class ArticleListView(ListView):
@property
def page_number(self):
+ #sxc 获取当前分页页码,支持从 URL 参数或 kwargs 中提取
page_kwarg = self.page_kwarg
page = self.kwargs.get(
page_kwarg) or self.request.GET.get(page_kwarg) or 1
@@ -51,12 +54,14 @@ class ArticleListView(ListView):
"""
子类重写.获得queryset的缓存key
"""
+ #sxc 抽象方法,子类需重写以定义查询集的缓存键生成规则,用于缓存数据的标识
raise NotImplementedError()
def get_queryset_data(self):
"""
子类重写.获取queryset的数据
"""
+ #sxc 抽象方法,子类需重写以实现具体的查询集数据获取逻辑(如筛选文章条件)
raise NotImplementedError()
def get_queryset_from_cache(self, cache_key):
@@ -65,6 +70,7 @@ class ArticleListView(ListView):
:param cache_key: 缓存key
:return:
'''
+ #sxc 从缓存中获取查询集数据,若缓存未命中则调用 get_queryset_data 获取并缓存
value = cache.get(cache_key)
if value:
logger.info('get view cache.key:{key}'.format(key=cache_key))
@@ -80,6 +86,7 @@ class ArticleListView(ListView):
重写默认,从缓存获取数据
:return:
'''
+ #sxc 重写父类方法,优先从缓存获取查询集数据,减少数据库查询
key = self.get_queryset_cache_key()
value = self.get_queryset_from_cache(key)
return value
@@ -94,13 +101,16 @@ class IndexView(ArticleListView):
首页
'''
# 友情链接类型
+ #sxc 博客首页视图,继承自 ArticleListView 基类,专注于展示所有已发布文章,配置首页专属友情链接类型
link_type = LinkShowType.I
def get_queryset_data(self):
+ #sxc 实现父类抽象方法,获取首页需展示的文章数据:筛选类型为 “普通文章(a)” 且状态为 “已发布(p)” 的文章
article_list = Article.objects.filter(type='a', status='p')
return article_list
def get_queryset_cache_key(self):
+ #sxc 实现父类抽象方法,生成首页文章列表的缓存键,格式为 “index_页码”,确保不同分页数据的缓存隔离
cache_key = 'index_{page}'.format(page=self.page_number)
return cache_key
@@ -109,18 +119,21 @@ class ArticleDetailView(DetailView):
'''
文章详情页面
'''
- template_name = 'blog/article_detail.html'
- model = Article
- pk_url_kwarg = 'article_id'
- context_object_name = "article"
+ #sxc 文章详情页视图,继承自 Django 的 DetailView,负责单篇文章的完整信息展示,包括内容、评论、上下篇导航等
+ template_name = 'blog/article_detail.html' #sxc 指定详情页渲染模板路径
+ model = Article #sxc 关联的数据模型为 Article,自动从数据库获取文章数据
+ pk_url_kwarg = 'article_id' #sxc 指定 URL 中用于匹配文章主键的参数名为 “article_id”
+ context_object_name = "article" #sxc 设定模板中访问文章对象的变量名为 “article”
def get_context_data(self, **kwargs):
+ #sxc 重写父类方法,构建详情页所需的完整上下文数据,包括评论表单、评论分页、上下篇文章等
comment_form = CommentForm()
article_comments = self.object.comment_list()
parent_comments = article_comments.filter(parent_comment=None)
blog_setting = get_blog_setting()
paginator = Paginator(parent_comments, blog_setting.article_comment_count)
+ #sxc 处理评论分页页码:从 URL 参数获取,非数字 / 越界时自动修正为合法值
page = self.request.GET.get('comment_page', '1')
if not page.isnumeric():
page = 1
@@ -130,31 +143,34 @@ class ArticleDetailView(DetailView):
page = 1
if page > paginator.num_pages:
page = paginator.num_pages
-
+#sxc 获取当前页的评论数据,并计算上下页页码
p_comments = paginator.page(page)
next_page = p_comments.next_page_number() if p_comments.has_next() else None
prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None
-
+#sxc 构建评论上下页的跳转 URL,包含锚点 “#commentlist-container” 以定位到评论区
if next_page:
kwargs[
'comment_next_page_url'] = self.object.get_absolute_url() + f'?comment_page={next_page}#commentlist-container'
if prev_page:
kwargs[
'comment_prev_page_url'] = self.object.get_absolute_url() + f'?comment_page={prev_page}#commentlist-container'
+ #sxc 向上下文注入评论相关数据:表单、所有评论、当前页评论、评论总数
kwargs['form'] = comment_form
kwargs['article_comments'] = article_comments
kwargs['p_comments'] = p_comments
kwargs['comment_count'] = len(
article_comments) if article_comments else 0
-
+#sxc 向上下文注入当前文章的上下篇文章对象,用于导航
kwargs['next_article'] = self.object.next_article
kwargs['prev_article'] = self.object.prev_article
-
+#sxc 调用父类方法获取基础上下文,再扩展插件相关逻辑
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
# Action Hook, 通知插件"文章详情已获取"
+ #sxc 触发 “文章详情已获取” 的插件动作钩子,通知所有注册插件执行对应逻辑(如统计阅读量
hooks.run_action('after_article_body_get', article=article, request=self.request)
# # Filter Hook, 允许插件修改文章正文
+ #sxc 触发 “文章内容过滤” 的插件钩子,允许插件修改文章正文(如添加水印、解析特殊语法)
article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
request=self.request)
@@ -165,31 +181,36 @@ class CategoryDetailView(ArticleListView):
'''
分类目录列表
'''
+ #sxc 分类目录文章列表视图,按 “分类” 维度筛选文章,展示指定分类及下属子分类的所有已发布文章
page_type = "分类目录归档"
def get_queryset_data(self):
+ #sxc 实现父类抽象方法,按分类筛选文章:先获取分类对象,再查询该分类及子分类下的已发布文章
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
-
+#sxc 获取当前分类及所有子分类的名称列表(用于多分类联合筛选)
categoryname = category.name
self.categoryname = categoryname
categorynames = list(
map(lambda c: c.name, category.get_sub_categorys()))
+ #sxc 筛选 “属于目标分类 / 子分类” 且 “状态为已发布(p)” 的文章
article_list = Article.objects.filter(
category__name__in=categorynames, status='p')
return article_list
def get_queryset_cache_key(self):
+ #sxc 实现父类抽象方法,生成分类列表的专属缓存键,确保不同分类、不同分页的缓存隔离
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
+ #sxc 缓存键格式:category_list_分类名_页码(如 “category_list_技术_2”)
cache_key = 'category_list_{categoryname}_{page}'.format(
categoryname=categoryname, page=self.page_number)
return cache_key
def get_context_data(self, **kwargs):
-
+#sxc 重写父类方法,向模板上下文注入分类相关标识,用于页面个性化渲染
categoryname = self.categoryname
try:
categoryname = categoryname.split('/')[-1]
@@ -204,9 +225,11 @@ class AuthorDetailView(ArticleListView):
'''
作者详情页
'''
+ #sxc 作者文章列表视图,按 “作者” 维度筛选文章,展示指定作者发布的所有已发布文章
page_type = '作者文章归档'
def get_queryset_cache_key(self):
+ #sxc 实现父类抽象方法,生成作者列表的专属缓存键,结合作者别名和页码确保缓存隔离
from uuslug import slugify
author_name = slugify(self.kwargs['author_name'])
cache_key = 'author_{author_name}_{page}'.format(
@@ -214,12 +237,15 @@ class AuthorDetailView(ArticleListView):
return cache_key
def get_queryset_data(self):
+ #sxc 实现父类抽象方法,按作者筛选文章:查询指定作者发布的已发布文章
author_name = self.kwargs['author_name']
+ #sxc 筛选 “作者用户名为目标值”“类型为普通文章(a)”“状态为已发布(p)” 的文章
article_list = Article.objects.filter(
author__username=author_name, type='a', status='p')
return article_list
def get_context_data(self, **kwargs):
+ #sxc 重写父类方法,向上下文注入作者相关标识,用于页面渲染
author_name = self.kwargs['author_name']
kwargs['page_type'] = AuthorDetailView.page_type
kwargs['tag_name'] = author_name
@@ -230,6 +256,7 @@ class TagDetailView(ArticleListView):
'''
标签列表页面
'''
+ #sxc 标签文章列表视图,按 “标签” 维度筛选文章,展示关联指定标签的所有已发布文章
page_type = '分类标签归档'
def get_queryset_data(self):
@@ -237,11 +264,13 @@ class TagDetailView(ArticleListView):
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
+ #sxc 筛选 “关联目标标签”“类型为普通文章(a)”“状态为已发布(p)” 的文章
article_list = Article.objects.filter(
tags__name=tag_name, type='a', status='p')
return article_list
def get_queryset_cache_key(self):
+ #sxc 实现父类抽象方法,生成标签列表的专属缓存键,确保不同标签、不同分页的缓存隔离
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
@@ -251,6 +280,7 @@ class TagDetailView(ArticleListView):
return cache_key
def get_context_data(self, **kwargs):
+ #sxc 重写父类方法,向上下文注入标签相关标识,用于页面个性化渲染
# tag_name = self.kwargs['tag_name']
tag_name = self.name
kwargs['page_type'] = TagDetailView.page_type
@@ -262,29 +292,36 @@ class ArchivesView(ArticleListView):
'''
文章归档页面
'''
+ #sxc 文章归档页面视图,展示所有已发布文章的归档信息(按时间等维度聚合),不分页
page_type = '文章归档'
paginate_by = None
page_kwarg = None
template_name = 'blog/article_archives.html'
def get_queryset_data(self):
+ #sxc 获取所有状态为 “已发布(p)” 的文章,用于归档展示
return Article.objects.filter(status='p').all()
def get_queryset_cache_key(self):
+ #sxc 归档页缓存键固定为 'archives'(因不分页,无需页码参数)
cache_key = 'archives'
return cache_key
class LinkListView(ListView):
+ #sxc 友情链接列表视图,展示所有启用的友情链接
model = Links
template_name = 'blog/links_list.html'
def get_queryset(self):
+ #sxc 筛选出所有 “已启用(is_enable=True)” 的友情链接
return Links.objects.filter(is_enable=True)
class EsSearchView(SearchView):
+ #sxc 基于 Elasticsearch 的搜索视图,处理用户搜索请求并返回带分页和拼写建议的结果
def get_context(self):
+ #sxc 构建搜索结果页面的上下文数据,包含查询词、分页信息和拼写建议
paginator, page = self.build_page()
context = {
"query": self.query,
@@ -293,6 +330,7 @@ class EsSearchView(SearchView):
"paginator": paginator,
"suggestion": None,
}
+ #sxc 若搜索引擎支持拼写纠错,获取并添加拼写建议
if hasattr(self.results, "query") and self.results.query.backend.include_spelling:
context["suggestion"] = self.results.query.get_spelling_suggestion()
context.update(self.extra_context())
@@ -307,46 +345,57 @@ def fileupload(request):
:param request:
:return:
"""
+ #sxc 处理文件上传(图床功能),支持图片压缩和签名验证,仅允许 POST 请求
if request.method == 'POST':
- sign = request.GET.get('sign', None)
+ sign = request.GET.get('sign', None) #sxc 获取 URL 中的签名参数(用于权限验证)
+ #sxc 验证签名合法性:签名不存在或与系统计算值不符则拒绝访问
if not sign:
- return HttpResponseForbidden()
+ return HttpResponseForbidden() #sxc 无签名时返回 403
if not sign == get_sha256(get_sha256(settings.SECRET_KEY)):
- return HttpResponseForbidden()
- response = []
- for filename in request.FILES:
- timestr = timezone.now().strftime('%Y/%m/%d')
- imgextensions = ['jpg', 'png', 'jpeg', 'bmp']
- fname = u''.join(str(filename))
+ return HttpResponseForbidden() #sxc 签名不匹配时返回 403
+ response = [] #sxc 存储上传文件的访问 URL 列表
+ for filename in request.FILES: #sxc 遍历所有上传的文件
+ timestr = timezone.now().strftime('%Y/%m/%d')#sxc 按当前日期生成存储目录(如 2023/10/01)
+ imgextensions = ['jpg', 'png', 'jpeg', 'bmp'] #sxc 支持的图片文件扩展名
+ fname = u''.join(str(filename)) #sxc 处理文件名(确保 Unicode 兼容)
+ #sxc 判断文件是否为图片(检查扩展名是否在图片格式列表中)
isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
+ #sxc 确定文件存储根目录:图片存于 image 文件夹,其他文件存于 files 文件夹
base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr)
if not os.path.exists(base_dir):
os.makedirs(base_dir)
+ #sxc 生成唯一文件名(UUID + 原文件扩展名),避免重名
savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}"))
+ #sxc 防止路径遍历攻击:验证保存路径是否在基础目录内
if not savepath.startswith(base_dir):
return HttpResponse("only for post")
+ #sxc 将上传的文件分块写入本地存储
with open(savepath, 'wb+') as wfile:
for chunk in request.FILES[filename].chunks():
wfile.write(chunk)
+ #sxc 若为图片文件,压缩图片质量(优化存储和加载速度)
if isimage:
from PIL import Image
image = Image.open(savepath)
- image.save(savepath, quality=20, optimize=True)
+ image.save(savepath, quality=20, optimize=True)#sxc 质量压缩至 20%
+ #sxc 生成文件的访问 URL 并添加到响应列表
url = static(savepath)
response.append(url)
- return HttpResponse(response)
+ return HttpResponse(response) #sxc 返回所有上传文件的 URL 列表
else:
- return HttpResponse("only for post")
+ return HttpResponse("only for post") #sxc 非 POST 请求直接拒绝
def page_not_found_view(
request,
exception,
template_name='blog/error_page.html'):
+ #sxc 处理 404 页面未找到错误,记录异常并返回自定义错误页面
if exception:
logger.error(exception)
url = request.get_full_path()
+ #sxc 渲染错误页面,传递错误信息和状态码
return render(request,
template_name,
{'message': _('Sorry, the page you requested is not found, please click the home page to see other?'),
@@ -355,6 +404,7 @@ def page_not_found_view(
def server_error_view(request, template_name='blog/error_page.html'):
+ #sxc 处理 500 服务器内部错误,返回自定义错误页面
return render(request,
template_name,
{'message': _('Sorry, the server is busy, please click the home page to see other?'),
@@ -366,6 +416,7 @@ def permission_denied_view(
request,
exception,
template_name='blog/error_page.html'):
+ #sxc 处理 403 权限拒绝错误,记录异常并返回自定义错误页面
if exception:
logger.error(exception)
return render(
@@ -375,5 +426,6 @@ def permission_denied_view(
def clean_cache_view(request):
- cache.clear()
+ #sxc 清理系统缓存的视图,调用缓存清理方法并返回成功响应
+ cache.clear() #sxc 清除所有缓存数据
return HttpResponse('ok')