diff --git a/.idea/djangoBlogStudy.iml b/.idea/djangoBlogStudy.iml index e378968..566506b 100644 --- a/.idea/djangoBlogStudy.iml +++ b/.idea/djangoBlogStudy.iml @@ -2,7 +2,7 @@ - + diff --git a/src/djangoblog/__init__.py b/src/djangoblog/__init__.py index 8704cf6..b04347d 100644 --- a/src/djangoblog/__init__.py +++ b/src/djangoblog/__init__.py @@ -1,3 +1,4 @@ +#wwc # 指定该 Django 应用的默认配置类。 # 'default_app_config' 是一个特殊变量,Django 在加载应用时会自动识别它, # 并使用指定的 AppConfig 子类来配置该应用的行为。 diff --git a/src/djangoblog/admin_site.py b/src/djangoblog/admin_site.py index 1d40441..213d792 100644 --- a/src/djangoblog/admin_site.py +++ b/src/djangoblog/admin_site.py @@ -5,20 +5,20 @@ from django.contrib.sites.admin import SiteAdmin from django.contrib.sites.models import Site # 导入各应用的 admin 配置和模型 -from accounts.admin import * # 用户管理后台配置 -from blog.admin import * # 博客功能后台配置 -from blog.models import * # 博客功能模型 -from comments.admin import * # 评论功能后台配置 -from comments.models import * # 评论功能模型 -from djangoblog.logentryadmin import LogEntryAdmin # 自定义的日志条目管理类 -from oauth.admin import * # 第三方登录后台配置 -from oauth.models import * # 第三方登录模型 -from owntracks.admin import * # OwnTracks 位置追踪后台配置 -from owntracks.models import * # OwnTracks 模型 -from servermanager.admin import * # 服务器命令管理后台配置 -from servermanager.models import * # 服务器命令模型 - - +from ..accounts.admin import * # 用户管理后台配置 +from ..blog.admin import * # 博客功能后台配置 +from ..blog.models import * # 博客功能模型 +from ..comments.admin import * # 评论功能后台配置 +from ..comments.models import * # 评论功能模型 +from ..djangoblog.logentryadmin import LogEntryAdmin # 自定义的日志条目管理类 +from ..oauth.admin import * # 第三方登录后台配置 +from ..oauth.models import * # 第三方登录模型 +from ..owntracks.admin import * # OwnTracks 位置追踪后台配置 +from ..owntracks.models import * # OwnTracks 模型 +from ..servermanager.admin import * # 服务器命令管理后台配置 +from ..servermanager.models import * # 服务器命令模型 + +#wwc class DjangoBlogAdminSite(AdminSite): """ 自定义 Django 管理后台站点类,用于替代默认的 admin.site。 @@ -35,6 +35,7 @@ class DjangoBlogAdminSite(AdminSite): site_header = 'djangoblog administration' # 浏览器标签和页面标题 site_title = 'djangoblog site admin' # 页面中显示的主标题 +#wwc def __init__(self, name='admin'): """ 初始化自定义管理站点。 @@ -45,6 +46,7 @@ class DjangoBlogAdminSite(AdminSite): """ super().__init__(name) # 调用父类初始化方法 +#wwc def has_permission(self, request): """ 重写权限检查方法,控制谁可以访问此管理后台。 diff --git a/src/djangoblog/apps.py b/src/djangoblog/apps.py index 58f329f..99851fc 100644 --- a/src/djangoblog/apps.py +++ b/src/djangoblog/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig - +#wwc class DjangoblogAppConfig(AppConfig): """ Django 应用配置类,用于定义 'djangoblog' 应用的配置信息。 @@ -17,7 +17,7 @@ class DjangoblogAppConfig(AppConfig): # 指定该应用的完整 Python 导入路径。 # 必须与应用在项目中的实际路径一致,Django 通过此属性识别应用。 name = 'djangoblog' - +#wwc def ready(self): """ 应用准备就绪时调用的方法。 diff --git a/src/djangoblog/blog_signals.py b/src/djangoblog/blog_signals.py index 27a4a68..56c9ab1 100644 --- a/src/djangoblog/blog_signals.py +++ b/src/djangoblog/blog_signals.py @@ -9,12 +9,12 @@ from django.core.mail import EmailMultiAlternatives from django.db.models.signals import post_save from django.dispatch import receiver -from comments.models import Comment -from comments.utils import send_comment_email -from djangoblog.spider_notify import SpiderNotify -from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache -from djangoblog.utils import get_current_site -from oauth.models import OAuthUser +from ..comments.models import Comment +from ..comments.utils import send_comment_email +from ..djangoblog.spider_notify import SpiderNotify +from ..djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache +from ..djangoblog.utils import get_current_site +from ..oauth.models import OAuthUser # 获取当前模块的日志记录器,用于记录信号处理过程中的运行信息和错误 logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ oauth_user_login_signal = django.dispatch.Signal(['id']) # - content: 邮件正文(HTML) send_email_signal = django.dispatch.Signal(['emailto', 'title', 'content']) - +#wwc @receiver(send_email_signal) def send_email_signal_handler(sender, **kwargs): """ @@ -65,7 +65,7 @@ def send_email_signal_handler(sender, **kwargs): msg.content_subtype = "html" # 设置内容类型为 HTML # 创建邮件发送日志对象 - from servermanager.models import EmailSendLog + from ..servermanager.models import EmailSendLog log = EmailSendLog() log.title = title log.content = content @@ -82,7 +82,7 @@ def send_email_signal_handler(sender, **kwargs): # 无论成功或失败,都保存日志记录 log.save() - +#wwc @receiver(oauth_user_login_signal) def oauth_user_login_signal_handler(sender, **kwargs): """ @@ -110,7 +110,7 @@ def oauth_user_login_signal_handler(sender, **kwargs): # 登录后清理侧边栏缓存,确保用户信息等显示最新状态 delete_sidebar_cache() - +#wwc @receiver(post_save) def model_post_save_callback( sender, @@ -197,7 +197,7 @@ def model_post_save_callback( if clearcache: cache.clear() - +#wwc @receiver(user_logged_in) @receiver(user_logged_out) def user_auth_callback(sender, request, user, **kwargs): diff --git a/src/djangoblog/elasticsearch_backend.py b/src/djangoblog/elasticsearch_backend.py index 2aabfae..3bac2ba 100644 --- a/src/djangoblog/elasticsearch_backend.py +++ b/src/djangoblog/elasticsearch_backend.py @@ -5,20 +5,20 @@ from haystack.forms import ModelSearchForm from haystack.models import SearchResult from haystack.utils import log as logging -from blog.documents import ArticleDocument, ArticleDocumentManager -from blog.models import Article +from ..blog.documents import ArticleDocument, ArticleDocumentManager +from ..blog.models import Article # 获取当前模块的日志记录器,用于记录搜索相关的日志信息 logger = logging.getLogger(__name__) - +#wwc class ElasticSearchBackend(BaseSearchBackend): """ 自定义 Elasticsearch 搜索后端,继承自 Haystack 的 BaseSearchBackend。 负责与 Elasticsearch 交互,实现索引的创建、更新、删除、查询等操作。 使用自定义的 ArticleDocumentManager 来管理文档索引。 """ - +#wwc def __init__(self, connection_alias, **connection_options): """ 初始化搜索后端。 @@ -30,7 +30,7 @@ class ElasticSearchBackend(BaseSearchBackend): super(ElasticSearchBackend, self).__init__(connection_alias, **connection_options) self.manager = ArticleDocumentManager() # 实例化文档管理器 self.include_spelling = True # 启用拼写建议功能 - +#wwc def _get_models(self, iterable): """ 将模型实例或查询集转换为可索引的文档对象列表。 @@ -44,7 +44,7 @@ class ElasticSearchBackend(BaseSearchBackend): models = iterable if iterable and iterable[0] else Article.objects.all() docs = self.manager.convert_to_doc(models) return docs - +#wwc def _create(self, models): """ 创建新的索引并填充数据。 @@ -55,7 +55,7 @@ class ElasticSearchBackend(BaseSearchBackend): self.manager.create_index() # 创建索引结构 docs = self._get_models(models) # 转换为文档 self.manager.rebuild(docs) # 重建索引数据 - +#wwc def _delete(self, models): """ 从索引中删除指定的文档。 @@ -69,7 +69,7 @@ class ElasticSearchBackend(BaseSearchBackend): for m in models: m.delete() # 调用文档对象的 delete 方法 return True - +#wwc def _rebuild(self, models): """ 重建索引。如果 models 为空,则重建所有文章索引。 @@ -80,7 +80,7 @@ class ElasticSearchBackend(BaseSearchBackend): models = models if models else Article.objects.all() docs = self.manager.convert_to_doc(models) self.manager.update_docs(docs) # 更新文档到索引 - +#wwc def update(self, index, iterable, commit=True): """ 更新索引中的文档。 @@ -92,7 +92,7 @@ class ElasticSearchBackend(BaseSearchBackend): """ models = self._get_models(iterable) self.manager.update_docs(models) # 将转换后的文档更新到索引 - +#wwc def remove(self, obj_or_string): """ 从索引中移除单个对象或字符串。 @@ -102,7 +102,7 @@ class ElasticSearchBackend(BaseSearchBackend): """ models = self._get_models([obj_or_string]) self._delete(models) - +#wwc def clear(self, models=None, commit=True): """ 清空整个索引(删除所有文档)。 @@ -112,7 +112,7 @@ class ElasticSearchBackend(BaseSearchBackend): commit: 是否立即提交(未使用) """ self.remove(None) # 调用 remove 方法清空 - +#wwc @staticmethod def get_suggestion(query: str) -> str: """ @@ -138,7 +138,7 @@ class ElasticSearchBackend(BaseSearchBackend): else: keywords.append(suggest["text"]) # 无建议则使用原词 return ' '.join(keywords) - +#wwc @log_query def search(self, query_string, **kwargs): """ @@ -208,13 +208,13 @@ class ElasticSearchBackend(BaseSearchBackend): 'spelling_suggestion': spelling_suggestion, # 拼写建议 } - +#wwc class ElasticSearchQuery(BaseSearchQuery): """ 自定义搜索查询类,继承自 Haystack 的 BaseSearchQuery。 负责构建和处理搜索查询语句。 """ - +#wwc def _convert_datetime(self, date): """ 将日期时间对象转换为字符串格式,用于索引查询。 @@ -229,7 +229,7 @@ class ElasticSearchQuery(BaseSearchQuery): return force_str(date.strftime('%Y%m%d%H%M%S')) else: return force_str(date.strftime('%Y%m%d000000')) - +#wwc def clean(self, query_fragment): """ 清理用户输入的查询片段,防止特殊字符引发语法错误。 @@ -256,7 +256,7 @@ class ElasticSearchQuery(BaseSearchQuery): cleaned_words.append(word) return ' '.join(cleaned_words) - +#wwc def build_query_fragment(self, field, filter_type, value): """ 构建查询片段。此处直接返回 value.query_string。 @@ -270,7 +270,7 @@ class ElasticSearchQuery(BaseSearchQuery): str: 查询字符串 """ return value.query_string - +#wwc def get_count(self): """ 获取搜索结果总数。 @@ -280,7 +280,7 @@ class ElasticSearchQuery(BaseSearchQuery): """ results = self.get_results() return len(results) if results else 0 - +#wwc def get_spelling_suggestion(self, preferred_query=None): """ 获取拼写建议。 @@ -292,7 +292,7 @@ class ElasticSearchQuery(BaseSearchQuery): str: 拼写建议 """ return self._spelling_suggestion - +#wwc def build_params(self, spelling_query=None): """ 构建传递给后端的参数字典。 @@ -306,13 +306,13 @@ class ElasticSearchQuery(BaseSearchQuery): kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query) return kwargs - +#wwc class ElasticSearchModelSearchForm(ModelSearchForm): """ 自定义搜索表单,继承自 Haystack 的 ModelSearchForm。 用于处理用户提交的搜索请求,支持启用/禁用拼写建议。 """ - +#wwc def search(self): """ 重写 search 方法,在搜索前设置是否启用拼写建议。 @@ -325,7 +325,7 @@ class ElasticSearchModelSearchForm(ModelSearchForm): sqs = super().search() return sqs - +#wwc class ElasticSearchEngine(BaseEngine): """ 自定义搜索引擎,集成 Backend、Query 和 Form。 diff --git a/src/djangoblog/feeds.py b/src/djangoblog/feeds.py index 05b4c0d..f7caeb0 100644 --- a/src/djangoblog/feeds.py +++ b/src/djangoblog/feeds.py @@ -3,10 +3,10 @@ from django.contrib.syndication.views import Feed from django.utils import timezone from django.utils.feedgenerator import Rss201rev2Feed -from blog.models import Article -from djangoblog.utils import CommonMarkdown - +from ..blog.models import Article +from ..djangoblog.utils import CommonMarkdown +#wwc class DjangoBlogFeed(Feed): """ RSS 订阅源生成器,继承自 Django 的 Feed 类。 @@ -24,7 +24,7 @@ class DjangoBlogFeed(Feed): # 订阅源的相对 URL 路径 link = "/feed/" - +#wwc def author_name(self): """ 获取订阅源作者的名称。 @@ -35,7 +35,7 @@ class DjangoBlogFeed(Feed): str: 作者昵称(nickname) """ return get_user_model().objects.first().nickname - +#wwc def author_link(self): """ 获取订阅源作者的个人主页链接。 @@ -46,7 +46,7 @@ class DjangoBlogFeed(Feed): str: 作者主页的 URL """ return get_user_model().objects.first().get_absolute_url() - +#wwc def items(self): """ 获取订阅源包含的文章列表。 @@ -58,7 +58,7 @@ class DjangoBlogFeed(Feed): QuerySet: 包含最多 5 篇最新文章的查询集 """ return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5] - +#wwc def item_title(self, item): """ 获取每篇文章在订阅源中的标题。 @@ -70,7 +70,7 @@ class DjangoBlogFeed(Feed): str: 文章标题 """ return item.title - +#wwc def item_description(self, item): """ 获取每篇文章在订阅源中的描述内容。 @@ -85,7 +85,7 @@ class DjangoBlogFeed(Feed): str: 渲染后的 HTML 内容 """ return CommonMarkdown.get_markdown(item.body) - +#wwc def feed_copyright(self): """ 获取订阅源的版权信息。 @@ -97,7 +97,7 @@ class DjangoBlogFeed(Feed): """ now = timezone.now() return "Copyright© {year} 且听风吟".format(year=now.year) - +#wwc def item_link(self, item): """ 获取每篇文章在订阅源中的链接。 @@ -111,7 +111,7 @@ class DjangoBlogFeed(Feed): str: 文章的绝对 URL """ return item.get_absolute_url() - +#wwc def item_guid(self, item): """ 获取每篇文章的全局唯一标识符(GUID)。 diff --git a/src/djangoblog/logentryadmin.py b/src/djangoblog/logentryadmin.py index ada37ef..8da123a 100644 --- a/src/djangoblog/logentryadmin.py +++ b/src/djangoblog/logentryadmin.py @@ -17,7 +17,7 @@ from django.utils.translation import gettext_lazy as _ # - mark_safe: 标记字符串为安全 HTML(不转义) # - gettext_lazy: 国际化文本标记 - +#wwc class LogEntryAdmin(admin.ModelAdmin): """ 自定义 Django 管理后台中日志条目(LogEntry)的管理界面。 @@ -50,7 +50,7 @@ class LogEntryAdmin(admin.ModelAdmin): 'object_link', # 被操作的对象实例(带链接) 'get_change_message', # 操作详情消息 ] - +#wwc def has_add_permission(self, request): """ 控制是否允许添加新的日志条目。 @@ -64,7 +64,7 @@ class LogEntryAdmin(admin.ModelAdmin): bool: 始终返回 False,禁止添加权限 """ return False - +#wwc def has_change_permission(self, request, obj=None): """ 控制是否允许修改日志条目。 @@ -83,7 +83,7 @@ class LogEntryAdmin(admin.ModelAdmin): request.user.is_superuser or request.user.has_perm('admin.change_logentry') ) and request.method != 'POST' - +#wwc def has_delete_permission(self, request, obj=None): """ 控制是否允许删除日志条目。 @@ -98,7 +98,7 @@ class LogEntryAdmin(admin.ModelAdmin): bool: 始终返回 False,禁止删除权限 """ return False - +#wwc def object_link(self, obj): """ 将被操作对象的名称转换为可点击的超链接(如果可能)。 @@ -133,7 +133,7 @@ class LogEntryAdmin(admin.ModelAdmin): object_link.admin_order_field = 'object_repr' # 设置此字段在列表页显示的列标题(支持国际化) object_link.short_description = _('object') - +#wwc def user_link(self, obj): """ 将操作用户的名称转换为可点击的超链接,指向该用户在管理后台的编辑页面。 @@ -163,7 +163,7 @@ class LogEntryAdmin(admin.ModelAdmin): user_link.admin_order_field = 'user' # 设置此字段在列表页显示的列标题(支持国际化) user_link.short_description = _('user') - +#wwc def get_queryset(self, request): """ 自定义查询集,优化数据库查询性能。 @@ -179,7 +179,7 @@ class LogEntryAdmin(admin.ModelAdmin): """ queryset = super(LogEntryAdmin, self).get_queryset(request) return queryset.prefetch_related('content_type') - +#wwc def get_actions(self, request): """ 自定义可用的操作列表(如批量删除)。 diff --git a/src/djangoblog/plugin_manage/base_plugin.py b/src/djangoblog/plugin_manage/base_plugin.py index 2b4be5c..5460f0e 100644 --- a/src/djangoblog/plugin_manage/base_plugin.py +++ b/src/djangoblog/plugin_manage/base_plugin.py @@ -3,18 +3,21 @@ import logging logger = logging.getLogger(__name__) +#wwc class BasePlugin: # 插件元数据 PLUGIN_NAME = None PLUGIN_DESCRIPTION = None PLUGIN_VERSION = None +#wwc def __init__(self): if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]): raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.") self.init_plugin() self.register_hooks() +#wwc def init_plugin(self): """ 插件初始化逻辑 @@ -22,6 +25,7 @@ class BasePlugin: """ logger.info(f'{self.PLUGIN_NAME} initialized.') +#wwc def register_hooks(self): """ 注册插件钩子 @@ -29,6 +33,7 @@ class BasePlugin: """ pass +#wwc def get_plugin_info(self): """ 获取插件信息 diff --git a/src/djangoblog/plugin_manage/hook_constants.py b/src/djangoblog/plugin_manage/hook_constants.py index 1016b7f..54c679e 100644 --- a/src/djangoblog/plugin_manage/hook_constants.py +++ b/src/djangoblog/plugin_manage/hook_constants.py @@ -1,3 +1,4 @@ +#wwc # 定义一个常量,表示“加载文章详情”的动作类型或事件标识。 # 通常用于状态管理、事件分发或日志记录中,标识当前操作是获取或展示某篇文章的详细内容。 ARTICLE_DETAIL_LOAD = 'article_detail_load' diff --git a/src/djangoblog/plugin_manage/hooks.py b/src/djangoblog/plugin_manage/hooks.py index d712540..67667df 100644 --- a/src/djangoblog/plugin_manage/hooks.py +++ b/src/djangoblog/plugin_manage/hooks.py @@ -4,7 +4,7 @@ logger = logging.getLogger(__name__) _hooks = {} - +#wwc def register(hook_name: str, callback: callable): """ 注册一个钩子回调。 @@ -14,7 +14,7 @@ def register(hook_name: str, callback: callable): _hooks[hook_name].append(callback) logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'") - +#wwc def run_action(hook_name: str, *args, **kwargs): """ 执行一个 Action Hook。 @@ -28,7 +28,7 @@ def run_action(hook_name: str, *args, **kwargs): except Exception as e: logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) - +#wwc def apply_filters(hook_name: str, value, *args, **kwargs): """ 执行一个 Filter Hook。 diff --git a/src/djangoblog/plugin_manage/loader.py b/src/djangoblog/plugin_manage/loader.py index b82c49e..a60c27a 100644 --- a/src/djangoblog/plugin_manage/loader.py +++ b/src/djangoblog/plugin_manage/loader.py @@ -5,7 +5,7 @@ from django.conf import settings # 获取当前模块的日志记录器,用于输出插件加载过程中的日志信息(如成功或失败记录) logger = logging.getLogger(__name__) - +#wwc def load_plugins(): """ 动态加载并初始化插件。 diff --git a/src/djangoblog/sitemap.py b/src/djangoblog/sitemap.py index 9faf208..ce548ad 100644 --- a/src/djangoblog/sitemap.py +++ b/src/djangoblog/sitemap.py @@ -1,9 +1,9 @@ from django.contrib.sitemaps import Sitemap from django.urls import reverse -from blog.models import Article, Category, Tag - +from ..blog.models import Article, Category, Tag +#wwc class StaticViewSitemap(Sitemap): """ 静态页面的站点地图(Sitemap)类。 @@ -14,7 +14,7 @@ class StaticViewSitemap(Sitemap): priority = 0.5 # 默认更新频率:每天 changefreq = 'daily' - +#wwc def items(self): """ 定义需要包含在 sitemap 中的静态视图名称列表。 @@ -23,7 +23,7 @@ class StaticViewSitemap(Sitemap): list: 包含 Django URL 命名的列表,此处仅包含首页 'blog:index' """ return ['blog:index', ] - +#wwc def location(self, item): """ 根据 items() 返回的 URL 名称,生成对应的绝对路径。 @@ -36,7 +36,7 @@ class StaticViewSitemap(Sitemap): """ return reverse(item) - +#wwc class ArticleSiteMap(Sitemap): """ 文章(Article)模型的站点地图类。 @@ -47,7 +47,7 @@ class ArticleSiteMap(Sitemap): changefreq = "monthly" # 优先级:0.6(相对重要) priority = "0.6" - +#wwc def items(self): """ 获取所有已发布(status='p')的文章对象。 @@ -56,7 +56,7 @@ class ArticleSiteMap(Sitemap): QuerySet: 所有已发布文章的查询集 """ return Article.objects.filter(status='p') - +#wwc def lastmod(self, obj): """ 获取每篇文章的最后修改时间,用于搜索引擎判断内容更新。 @@ -69,7 +69,7 @@ class ArticleSiteMap(Sitemap): """ return obj.last_modify_time - +#wwc class CategorySiteMap(Sitemap): """ 分类(Category)模型的站点地图类。 @@ -80,7 +80,7 @@ class CategorySiteMap(Sitemap): changefreq = "Weekly" # 优先级:0.6 priority = "0.6" - +#wwc def items(self): """ 获取所有分类对象。 @@ -89,7 +89,7 @@ class CategorySiteMap(Sitemap): QuerySet: 所有分类的查询集 """ return Category.objects.all() - +#wwc def lastmod(self, obj): """ 获取每个分类的最后修改时间。 @@ -102,7 +102,7 @@ class CategorySiteMap(Sitemap): """ return obj.last_modify_time - +#wwc class TagSiteMap(Sitemap): """ 标签(Tag)模型的站点地图类。 @@ -113,7 +113,7 @@ class TagSiteMap(Sitemap): changefreq = "Weekly" # 优先级:0.3(相对较低) priority = "0.3" - +#wwc def items(self): """ 获取所有标签对象。 @@ -122,7 +122,7 @@ class TagSiteMap(Sitemap): QuerySet: 所有标签的查询集 """ return Tag.objects.all() - +#wwc def lastmod(self, obj): """ 获取每个标签的最后修改时间。 @@ -135,7 +135,7 @@ class TagSiteMap(Sitemap): """ return obj.last_modify_time - +#wwc class UserSiteMap(Sitemap): """ 用户(作者)的站点地图类。 @@ -146,7 +146,7 @@ class UserSiteMap(Sitemap): changefreq = "Weekly" # 优先级:0.3 priority = "0.3" - +#wwc def items(self): """ 获取所有发布过文章的作者(去重)。 @@ -164,7 +164,7 @@ class UserSiteMap(Sitemap): list: 去重后的用户(作者)对象列表 """ return list(set(map(lambda x: x.author, Article.objects.all()))) - +#wwc def lastmod(self, obj): """ 获取每个用户的最后修改时间。 diff --git a/src/djangoblog/spider_notify.py b/src/djangoblog/spider_notify.py index dfb8dbb..948323b 100644 --- a/src/djangoblog/spider_notify.py +++ b/src/djangoblog/spider_notify.py @@ -5,13 +5,13 @@ from django.conf import settings # 获取当前模块的日志记录器,用于输出日志信息 logger = logging.getLogger(__name__) - +#wwc class SpiderNotify(): """ 蜘蛛推送通知工具类,用于向搜索引擎(如百度)主动推送网站新内容的URL, 帮助搜索引擎更快发现和收录网页。 """ - +#wwc @staticmethod def baidu_notify(urls): """ @@ -40,7 +40,7 @@ class SpiderNotify(): logger.info(result.text) # 记录百度服务器返回的响应内容(如成功/失败信息) except Exception as e: logger.error(e) # 记录请求过程中发生的任何异常(如网络错误、超时等) - +#wwc @staticmethod def notify(url): """ diff --git a/src/djangoblog/tests.py b/src/djangoblog/tests.py index ddbfe31..c031fa0 100644 --- a/src/djangoblog/tests.py +++ b/src/djangoblog/tests.py @@ -1,9 +1,9 @@ from django.test import TestCase # 从项目工具模块导入所有工具函数和类,用于测试 -from djangoblog.utils import * - +from ..djangoblog.utils import * +#wwc class DjangoBlogTest(TestCase): """ DjangoBlog 应用的测试用例基类(目前仅包含工具函数测试)。 @@ -18,7 +18,7 @@ class DjangoBlogTest(TestCase): 若后续需要创建测试数据(如用户、文章等),可在此方法中完成。 """ pass - +#wwc def test_utils(self): """ 测试工具模块(djangoblog.utils)中的核心功能: diff --git a/src/djangoblog/urls.py b/src/djangoblog/urls.py index 880e40f..0b4ae3c 100644 --- a/src/djangoblog/urls.py +++ b/src/djangoblog/urls.py @@ -14,13 +14,13 @@ from django.urls import re_path # 支持正则表达式匹配的 URL 映射 from haystack.views import search_view_factory # Haystack 搜索框架的视图工厂 # 导入项目内自定义组件 -from blog.views import EsSearchView # 自定义的 Elasticsearch 搜索视图 -from djangoblog.admin_site import admin_site # 自定义的 Django 管理后台实例 -from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm # 搜索表单类 -from djangoblog.feeds import DjangoBlogFeed # RSS 订阅源 -from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap # 站点地图类 - +from ..blog.views import EsSearchView # 自定义的 Elasticsearch 搜索视图 +from ..djangoblog.admin_site import admin_site # 自定义的 Django 管理后台实例 +from ..djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm # 搜索表单类 +from ..djangoblog.feeds import DjangoBlogFeed # RSS 订阅源 +from ..djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap # 站点地图类 +#wwc # 定义站点地图(sitemap)的映射字典 # 将不同的 sitemap 类按类别注册,用于生成 sitemap.xml sitemaps = { diff --git a/src/djangoblog/utils.py b/src/djangoblog/utils.py index d2df35b..ad0a37c 100644 --- a/src/djangoblog/utils.py +++ b/src/djangoblog/utils.py @@ -34,11 +34,11 @@ def get_max_articleid_commentid(): 返回: tuple: (最新文章的主键, 最新评论的主键) """ - from blog.models import Article - from comments.models import Comment + from ..blog.models import Article + from ..comments.models import Comment return (Article.objects.latest().pk, Comment.objects.latest().pk) - +#wwc def get_sha256(str): """ 计算输入字符串的 SHA256 哈希值。 @@ -52,7 +52,7 @@ def get_sha256(str): m = sha256(str.encode('utf-8')) return m.hexdigest() - +#wwc def cache_decorator(expiration=3 * 60): """ 缓存装饰器,用于为函数添加缓存功能,避免重复计算或数据库查询。 @@ -70,7 +70,7 @@ def cache_decorator(expiration=3 * 60): 4. 未命中则执行原函数,将结果存入缓存并返回 5. 特殊处理返回值为 None 的情况,避免缓存穿透 """ - +#wwc def wrapper(func): def news(*args, **kwargs): try: @@ -103,7 +103,7 @@ def cache_decorator(expiration=3 * 60): return wrapper - +#wwc def expire_view_cache(path, servername, serverport, key_prefix=None): """ 手动清除特定视图的缓存(用于内容更新后刷新缓存)。 @@ -137,7 +137,7 @@ def expire_view_cache(path, servername, serverport, key_prefix=None): return True return False - +#wwc @cache_decorator() def get_current_site(): """ @@ -149,12 +149,12 @@ def get_current_site(): site = Site.objects.get_current() return site - +#wwc class CommonMarkdown: """ 提供统一的 Markdown 解析功能,支持代码高亮、目录生成等。 """ - +#wwc @staticmethod def _convert_markdown(value): """ @@ -177,7 +177,7 @@ class CommonMarkdown: body = md.convert(value) toc = md.toc return body, toc - +#wwc @staticmethod def get_markdown_with_toc(value): """ @@ -191,7 +191,7 @@ class CommonMarkdown: """ body, toc = CommonMarkdown._convert_markdown(value) return body, toc - +#wwc @staticmethod def get_markdown(value): """ @@ -206,7 +206,7 @@ class CommonMarkdown: body, toc = CommonMarkdown._convert_markdown(value) return body - +#wwc def send_email(emailto, title, content): """ 发送邮件的快捷方法,通过 Django 信号机制解耦。 @@ -219,14 +219,14 @@ def send_email(emailto, title, content): 实现: 触发自定义的 send_email_signal 信号,由信号处理器完成实际的邮件发送逻辑。 """ - from djangoblog.blog_signals import send_email_signal + from ..djangoblog.blog_signals import send_email_signal send_email_signal.send( send_email.__class__, emailto=emailto, title=title, content=content) - +#wwc def generate_code() -> str: """ 生成一个 6 位的随机数字验证码。 @@ -236,7 +236,7 @@ def generate_code() -> str: """ return ''.join(random.sample(string.digits, 6)) - +#wwc def parse_dict_to_url(dict): """ 将字典转换为 URL 查询参数字符串(键值对用 & 连接)。 @@ -255,7 +255,7 @@ def parse_dict_to_url(dict): for k, v in dict.items()]) return url - +#wwc def get_blog_setting(): """ 获取博客系统设置,优先从缓存读取,未命中则从数据库获取并缓存。 @@ -269,7 +269,7 @@ def get_blog_setting(): if value: return value else: - from blog.models import BlogSettings + from ..blog.models import BlogSettings if not BlogSettings.objects.count(): setting = BlogSettings() setting.site_name = 'djangoblog' @@ -291,7 +291,7 @@ def get_blog_setting(): cache.set('get_blog_setting', value) return value - +#wwc def save_user_avatar(url): """ 从指定 URL 下载用户头像并保存到本地静态文件目录。 @@ -329,7 +329,7 @@ def save_user_avatar(url): logger.error(e) return static('blog/img/avatar.png') - +#wwc def delete_sidebar_cache(): """ 清除侧边栏所有缓存。 @@ -340,13 +340,13 @@ def delete_sidebar_cache(): 实现: 遍历 LinkShowType 的所有值,删除以 'sidebar' 为前缀的缓存键。 """ - from blog.models import LinkShowType + from ..blog.models import LinkShowType keys = ["sidebar" + x for x in LinkShowType.values] for k in keys: logger.info('delete sidebar key:' + k) cache.delete(k) - +#wwc def delete_view_cache(prefix, keys): """ 删除基于模板片段缓存(@cache)的缓存。 @@ -362,7 +362,7 @@ def delete_view_cache(prefix, keys): key = make_template_fragment_key(prefix, keys) cache.delete(key) - +#wwc def get_resource_url(): """ 获取静态资源的基础 URL。 @@ -386,7 +386,7 @@ ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'l 'h2', 'p'] ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']} - +#wwc def sanitize_html(html): """ 清理 HTML 内容,移除不安全的标签和属性,防止跨站脚本(XSS)攻击。 diff --git a/src/djangoblog/whoosh_cn_backend.py b/src/djangoblog/whoosh_cn_backend.py index b55c41f..e8338dc 100644 --- a/src/djangoblog/whoosh_cn_backend.py +++ b/src/djangoblog/whoosh_cn_backend.py @@ -51,7 +51,7 @@ DATETIME_REGEX = re.compile( LOCALS = threading.local() LOCALS.RAM_STORE = None - +#wwc class WhooshHtmlFormatter(HtmlFormatter): """ 自定义高亮格式化器,使用简单标签包裹匹配关键词。 @@ -59,7 +59,7 @@ class WhooshHtmlFormatter(HtmlFormatter): """ template = '<%(tag)s>%(t)s' - +#wwc class WhooshSearchBackend(BaseSearchBackend): """ Whoosh 搜索后端实现类,负责与 Whoosh 引擎交互,执行索引、搜索、删除等操作。 @@ -79,7 +79,7 @@ class WhooshSearchBackend(BaseSearchBackend): '\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', '*', '?', ':', '.', ) - +#wwc def __init__(self, connection_alias, **connection_options): """ 初始化 Whoosh 后端连接。 @@ -100,7 +100,7 @@ class WhooshSearchBackend(BaseSearchBackend): "You must specify a 'PATH' in your settings for connection '%s'." % connection_alias) self.log = logging.getLogger('haystack') # 日志记录器 - +#wwc def setup(self): """ 初始化索引环境:创建目录、构建 schema、打开或创建索引。 @@ -143,7 +143,7 @@ class WhooshSearchBackend(BaseSearchBackend): self.index = self.storage.create_index(self.schema) self.setup_complete = True - +#wwc def build_schema(self, fields): """ 根据 Django 模型字段定义构建 Whoosh 的 Schema(索引结构)。 @@ -188,7 +188,7 @@ class WhooshSearchBackend(BaseSearchBackend): raise SearchBackendError("No fields were found in any search_indexes. Please correct this before attempting to search.") return (content_field_name, Schema(**schema_fields)) - +#wwc def update(self, index, iterable, commit=True): """ 更新索引文档(添加或更新)。 @@ -224,7 +224,7 @@ class WhooshSearchBackend(BaseSearchBackend): if len(iterable) > 0: writer.commit() # 提交写入 - +#wwc def remove(self, obj_or_string, commit=True): """ 从索引中删除一个文档。 @@ -243,7 +243,7 @@ class WhooshSearchBackend(BaseSearchBackend): if not self.silently_fail: raise self.log.error("Failed to remove document '%s' from Whoosh: %s", whoosh_id, e, exc_info=True) - +#wwc def clear(self, models=None, commit=True): """ 清空索引。可指定模型或清空全部。 @@ -273,7 +273,7 @@ class WhooshSearchBackend(BaseSearchBackend): self.log.error("Failed to clear Whoosh index of models '%s': %s", ','.join(models_to_delete), e, exc_info=True) else: self.log.error("Failed to clear Whoosh index: %s", e, exc_info=True) - +#wwc def delete_index(self): """ 彻底删除索引目录并重建。 @@ -283,7 +283,7 @@ class WhooshSearchBackend(BaseSearchBackend): elif not self.use_file_storage: self.storage.clean() self.setup() # 重新初始化 - +#wwc def optimize(self): """ 优化索引(合并段),提升搜索性能。 @@ -292,7 +292,7 @@ class WhooshSearchBackend(BaseSearchBackend): self.setup() self.index = self.index.refresh() self.index.optimize() - +#wwc def calculate_page(self, start_offset=0, end_offset=None): """ 计算分页参数(页码和每页数量),适配 Whoosh 的分页机制。 @@ -307,7 +307,7 @@ class WhooshSearchBackend(BaseSearchBackend): page_length = end_offset - start_offset page_num = int(start_offset / page_length) + 1 # Whoosh 页码从 1 开始 return page_num, page_length - +#wwc @log_query def search(self, query_string, sort_by=None, start_offset=0, end_offset=None, fields='', highlight=False, facets=None, date_facets=None, query_facets=None, narrow_queries=None, spelling_query=None, @@ -432,7 +432,7 @@ class WhooshSearchBackend(BaseSearchBackend): spelling_suggestion = self.create_spelling_suggestion(spelling_query or query_string) \ if self.include_spelling else None return {'results': [], 'hits': 0, 'spelling_suggestion': spelling_suggestion} - +#wwc def more_like_this(self, model_instance, additional_query_string=None, start_offset=0, end_offset=None, models=None, limit_to_registered_models=None, result_class=None, **kwargs): """ @@ -501,7 +501,7 @@ class WhooshSearchBackend(BaseSearchBackend): narrow_searcher.close() return results return {'results': [], 'hits': 0} - +#wwc def _process_results(self, raw_page, highlight=False, query_string='', spelling_query=None, result_class=None): """ 处理原始搜索结果,转换为 SearchResult 对象列表。 @@ -561,7 +561,7 @@ class WhooshSearchBackend(BaseSearchBackend): 'facets': facets, 'spelling_suggestion': spelling_suggestion, } - +#wwc def create_spelling_suggestion(self, query_string): """ 生成拼写纠错建议。 @@ -584,7 +584,7 @@ class WhooshSearchBackend(BaseSearchBackend): if suggestions: suggested_words.append(suggestions[0]) return ' '.join(suggested_words) - +#wwc def _from_python(self, value): """ 将 Python 值转换为 Whoosh 可索引的字符串。 @@ -601,7 +601,7 @@ class WhooshSearchBackend(BaseSearchBackend): else: value = force_str(value) return value - +#wwc def _to_python(self, value): """ 将 Whoosh 存储的值转换回 Python 类型。 @@ -625,7 +625,7 @@ class WhooshSearchBackend(BaseSearchBackend): pass return value - +#wwc class WhooshSearchQuery(BaseSearchQuery): """ Whoosh 查询构建器,负责将 Django 查询语法转换为 Whoosh 查询字符串。 @@ -637,7 +637,7 @@ class WhooshSearchQuery(BaseSearchQuery): return force_str(date.strftime('%Y%m%d%H%M%S')) else: return force_str(date.strftime('%Y%m%d000000')) - +#wwc def clean(self, query_fragment): """ 清理用户输入的查询片段,避免保留字和字符引发语法错误。 @@ -653,7 +653,7 @@ class WhooshSearchQuery(BaseSearchQuery): break cleaned_words.append(word) return ' '.join(cleaned_words) - +#wwc def build_query_fragment(self, field, filter_type, value): """ 构建单个查询片段,如 "title:django" 或 "pub_date:[20200101 TO 20201231]" @@ -729,7 +729,7 @@ class WhooshSearchQuery(BaseSearchQuery): return u"%s%s" % (index_fieldname, query_frag) - +#wwc class WhooshEngine(BaseEngine): """ Haystack 引擎注册类,绑定 Backend 和 Query 类。