/postcomment',
- views.CommentPostView.as_view(),
- name='postcomment'),# URL名称,模板中可通过{% url 'comments:postcomment' article_id %}调用
-]
diff --git a/src/DjangoBlog-master/comments/utils.py b/src/DjangoBlog-master/comments/utils.py
deleted file mode 100644
index 91629f0..0000000
--- a/src/DjangoBlog-master/comments/utils.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import logging
-
-from django.utils.translation import gettext_lazy as _
-
-from djangoblog.utils import get_current_site
-from djangoblog.utils import send_email
-
-logger = logging.getLogger(__name__) # 获取当前模块的日志记录器
-
-
-def send_comment_email(comment):
- """发送评论相关邮件的主函数"""
- site = get_current_site().domain# 获取当前站点域名
- subject = _('Thanks for your comment')# 邮件标题(国际化)
- article_url = f"https://{site}{comment.article.get_absolute_url()}" # 构建文章完整URL
-
- # 1. 给评论作者发送感谢邮件
- html_content = _("""Thank you very much for your comments on this site
- You can visit %(article_title)s
- to review your comments,
- Thank you again!
-
- If the link above cannot be opened, please copy this link to your browser.
- %(article_url)s""") % {'article_url': article_url, 'article_title': comment.article.title}
- tomail = comment.author.email# 收件人邮箱
- send_email([tomail], subject, html_content)
-
- # 2. 如果是回复评论,给被回复者发送通知邮件
- try:
- if comment.parent_comment:
- html_content = _("""Your comment on %(article_title)s
has
- received a reply.
%(comment_body)s
-
- go check it out!
-
- If the link above cannot be opened, please copy this link to your browser.
- %(article_url)s
- """) % {'article_url': article_url, 'article_title': comment.article.title,
- 'comment_body': comment.parent_comment.body}
- tomail = comment.parent_comment.author.email # 被回复者邮箱
- send_email([tomail], subject, html_content)
- except Exception as e:
- logger.error(e)#记录邮件发送异常
diff --git a/src/DjangoBlog-master/comments/views.py b/src/DjangoBlog-master/comments/views.py
deleted file mode 100644
index 1c6d6c7..0000000
--- a/src/DjangoBlog-master/comments/views.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Create your views here.
-from django.core.exceptions import ValidationError
-from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404
-from django.utils.decorators import method_decorator
-from django.views.decorators.csrf import csrf_protect
-from django.views.generic.edit import FormView
-
-from accounts.models import BlogUser
-from blog.models import Article
-from .forms import CommentForm
-from .models import Comment
-
-# 评论提交处理视图
-class CommentPostView(FormView):
- form_class = CommentForm# 指定使用的表单类
- template_name = 'blog/article_detail.html'# 表单无效时渲染的模板
-
- #为视图添加CSRF保护
- @method_decorator(csrf_protect)
- def dispatch(self, *args, **kwargs):
- return super(CommentPostView, self).dispatch(*args, **kwargs)
-
- # 处理GET请求:直接访问评论提交URL时重定向到文章详情页评论区
- def get(self, request, *args, **kwargs):
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
- url = article.get_absolute_url()
- return HttpResponseRedirect(url + "#comments")
-
- # 表单验证失败时的处理:重新渲染文章详情页并显示表单错误
- def form_invalid(self, form):
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
-
- return self.render_to_response({
- 'form': form,
- 'article': article
- })
-
- # 表单验证成功后的处理:保存评论并发送通知邮件
- def form_valid(self, form):
- """提交的数据验证合法后的逻辑"""
- user = self.request.user# 获取当前登录用户
- author = BlogUser.objects.get(pk=user.pk)# 获取完整的用户信息
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
-
- # 检查文章是否允许评论(评论状态或文章状态为关闭时抛出异常)
- if article.comment_status == 'c' or article.status == 'c':
- raise ValidationError("该文章评论已关闭.")
- # 创建评论实例但不保存到数据库
- comment = form.save(False)
- comment.article = article
- # 获取站点配置,判断评论是否需要审核
- from djangoblog.utils import get_blog_setting
- settings = get_blog_setting()
- if not settings.comment_need_review:
- comment.is_enable = True# 无需审核则直接启用评论
- comment.author = author# 设置评论作者
-
- # 处理父评论ID:如果存在则设置为回复评论
- if form.cleaned_data['parent_comment_id']:
- parent_comment = Comment.objects.get(
- pk=form.cleaned_data['parent_comment_id'])
- comment.parent_comment = parent_comment
- # 保存评论到数据库
- comment.save(True)
- # 重定向到文章详情页并定位到该评论锚点位置
- return HttpResponseRedirect(
- "%s#div-comment-%d" %
- (article.get_absolute_url(), comment.pk))
diff --git a/src/DjangoBlog-master/djangoblog/__init__.py b/src/DjangoBlog-master/djangoblog/__init__.py
deleted file mode 100644
index 3279070..0000000
--- a/src/DjangoBlog-master/djangoblog/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# Zxy指定默认的应用配置类
-default_app_config = 'djangoblog.apps.DjangoblogAppConfig'
diff --git a/src/DjangoBlog-master/djangoblog/admin_site.py b/src/DjangoBlog-master/djangoblog/admin_site.py
deleted file mode 100644
index a0cb357..0000000
--- a/src/DjangoBlog-master/djangoblog/admin_site.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Zxy导入 Django 的 AdminSite 类和其他相关模块
-from django.contrib.admin import AdminSite
-from django.contrib.admin.models import LogEntry
-from django.contrib.sites.admin import SiteAdmin
-from django.contrib.sites.models import Site
-
-# Zxy导入自定义模块和模型
-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 *
-from owntracks.models import *
-from servermanager.admin import *
-from servermanager.models import *
-
-# Zxy定义自定义 AdminSite 类
-class DjangoBlogAdminSite(AdminSite):
- # 自定义站点标题和头部
- site_header = 'djangoblog administration'
- site_title = 'djangoblog site admin'
-
- # 初始化方法
- def __init__(self, name='admin'):
- super().__init__(name)
-
- # 定义权限检查方法,仅允许超级用户访问
- def has_permission(self, request):
- return request.user.is_superuser
-
-# Zxy创建自定义 AdminSite 实例
-admin_site = DjangoBlogAdminSite(name='admin')
-
-# Zxy注册模型到自定义 AdminSite
-admin_site.register(Article, ArticleAdmin)
-admin_site.register(Category, CategoryAdmin)
-admin_site.register(Tag, TagAdmin)
-admin_site.register(Links, LinksAdmin)
-admin_site.register(SideBar, SideBarAdmin)
-admin_site.register(BlogSettings, BlogSettingsAdmin)
-
-admin_site.register(commands, CommandsAdmin)
-admin_site.register(EmailSendLog, EmailSendLogAdmin)
-
-admin_site.register(BlogUser, BlogUserAdmin)
-
-admin_site.register(Comment, CommentAdmin)
-
-admin_site.register(OAuthUser, OAuthUserAdmin)
-admin_site.register(OAuthConfig, OAuthConfigAdmin)
-
-admin_site.register(OwnTrackLog, OwnTrackLogsAdmin)
-
-admin_site.register(Site, SiteAdmin)
-
-admin_site.register(LogEntry, LogEntryAdmin)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/apps.py b/src/DjangoBlog-master/djangoblog/apps.py
deleted file mode 100644
index f902735..0000000
--- a/src/DjangoBlog-master/djangoblog/apps.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Zxy导入Django的AppConfig模块
-from django.apps import AppConfig
-
-# Zxy定义Djangoblog应用的配置类
-class DjangoblogAppConfig(AppConfig):
- # Zxy设置默认的自动字段类型
- default_auto_field = 'django.db.models.BigAutoField'
- # Zxy应用名称
- name = 'djangoblog'
-
- # Zxy重写ready方法,用于初始化应用
- def ready(self):
- super().ready() # Zxy调用父类的ready方法
- # Zxy导入并加载插件
- from .plugin_manage.loader import load_plugins
- load_plugins() # Zxy加载插件
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/blog_signals.py b/src/DjangoBlog-master/djangoblog/blog_signals.py
deleted file mode 100644
index 9ba3376..0000000
--- a/src/DjangoBlog-master/djangoblog/blog_signals.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# Zxy导入线程模块
-import _thread
-# Zxy导入日志模块
-import logging
-
-# Zxy导入Django的信号模块
-import django.dispatch
-# Zxy导入Django的配置模块
-from django.conf import settings
-# Zxy导入Django的LogEntry模型
-from django.contrib.admin.models import LogEntry
-# Zxy导入用户登录和登出信号
-from django.contrib.auth.signals import user_logged_in, user_logged_out
-# Zxy导入邮件发送模块
-from django.core.mail import EmailMultiAlternatives
-# Zxy导入模型保存信号
-from django.db.models.signals import post_save
-# Zxy导入信号接收器
-from django.dispatch import receiver
-
-# Zxy导入评论模型
-from comments.models import Comment
-# Zxy导入发送评论邮件的工具函数
-from comments.utils import send_comment_email
-# Zxy导入爬虫通知工具
-from djangoblog.spider_notify import SpiderNotify
-# Zxy导入缓存和缓存清理工具
-from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache
-# Zxy导入获取当前站点的工具函数
-from djangoblog.utils import get_current_site
-# Zxy导入OAuth用户模型
-from oauth.models import OAuthUser
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy定义OAuth用户登录信号
-oauth_user_login_signal = django.dispatch.Signal(['id'])
-# Zxy定义发送邮件信号
-send_email_signal = django.dispatch.Signal(['emailto', 'title', 'content'])
-
-# Zxy定义发送邮件信号的处理函数
-@receiver(send_email_signal)
-def send_email_signal_handler(sender, **kwargs):
- emailto = kwargs['emailto'] # Zxy收件人
- title = kwargs['title'] # Zxy邮件标题
- content = kwargs['content'] # Zxy邮件内容
-
- msg = EmailMultiAlternatives( # Zxy创建邮件对象
- title,
- content,
- from_email=settings.DEFAULT_FROM_EMAIL, # Zxy发件人
- to=emailto # Zxy收件人
- )
- msg.content_subtype = "html" # Zxy设置邮件内容类型为HTML
-
- from servermanager.models import EmailSendLog # Zxy导入邮件发送日志模型
- log = EmailSendLog() # Zxy创建日志记录
- log.title = title
- log.content = content
- log.emailto = ','.join(emailto) # Zxy记录收件人
-
- try:
- result = msg.send() # Zxy发送邮件
- log.send_result = result > 0 # Zxy记录发送结果
- except Exception as e:
- logger.error(f"失败邮箱号: {emailto}, {e}") # Zxy记录错误信息
- log.send_result = False
- log.save() # Zxy保存日志
-
-# Zxy定义OAuth用户登录信号的处理函数
-@receiver(oauth_user_login_signal)
-def oauth_user_login_signal_handler(sender, **kwargs):
- id = kwargs['id'] # Zxy获取用户ID
- oauthuser = OAuthUser.objects.get(id=id) # Zxy获取OAuth用户
- site = get_current_site().domain # Zxy获取当前站点域名
- if oauthuser.picture and not oauthuser.picture.find(site) >= 0: # Zxy检查头像URL是否包含当前站点域名
- from djangoblog.utils import save_user_avatar # Zxy导入保存头像的工具函数
- oauthuser.picture = save_user_avatar(oauthuser.picture) # Zxy保存用户头像
- oauthuser.save() # Zxy保存用户信息
-
- delete_sidebar_cache() # Zxy删除侧边栏缓存
-
-# Zxy定义模型保存后的回调函数
-@receiver(post_save)
-def model_post_save_callback(sender, instance, created, raw, using, update_fields, **kwargs):
- clearcache = False # Zxy标记是否需要清理缓存
- if isinstance(instance, LogEntry): # Zxy如果是LogEntry模型,直接返回
- return
- if 'get_full_url' in dir(instance): # Zxy检查是否有获取完整URL的方法
- is_update_views = update_fields == {'views'} # Zxy检查是否是更新浏览次数
- if not settings.TESTING and not is_update_views: # Zxy如果不是测试环境且不是更新浏览次数
- try:
- notify_url = instance.get_full_url() # Zxy获取完整URL
- SpiderNotify.baidu_notify([notify_url]) # Zxy通知百度爬虫
- except Exception as ex:
- logger.error("notify spider", ex) # Zxy记录错误信息
- if not is_update_views:
- clearcache = True # Zxy标记清理缓存
-
- if isinstance(instance, Comment): # Zxy如果是评论模型
- if instance.is_enable: # Zxy检查评论是否启用
- path = instance.article.get_absolute_url() # Zxy获取文章的绝对URL
- site = get_current_site().domain # Zxy获取当前站点域名
- if site.find(':') > 0: # Zxy去除端口号
- site = site[0:site.find(':')]
-
- expire_view_cache( # Zxy清理视图缓存
- path,
- servername=site,
- serverport=80,
- key_prefix='blogdetail'
- )
- if cache.get('seo_processor'): # Zxy清理SEO处理器缓存
- cache.delete('seo_processor')
- comment_cache_key = 'article_comments_{id}'.format(id=instance.article.id) # Zxy生成评论缓存键
- cache.delete(comment_cache_key) # Zxy清理评论缓存
- delete_sidebar_cache() # Zxy清理侧边栏缓存
- delete_view_cache('article_comments', [str(instance.article.pk)]) # Zxy清理文章评论缓存
-
- _thread.start_new_thread(send_comment_email, (instance,)) # Zxy异步发送评论邮件
-
- if clearcache: # Zxy清理缓存
- cache.clear()
-
-# Zxy定义用户登录和登出的回调函数
-@receiver(user_logged_in)
-@receiver(user_logged_out)
-def user_auth_callback(sender, request, user, **kwargs):
- if user and user.username: # Zxy检查用户是否登录
- logger.info(user) # Zxy记录用户信息
- delete_sidebar_cache() # Zxy清理侧边栏缓存
- # cache.clear() # Zxy清理所有缓存(暂时注释)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py b/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py
deleted file mode 100644
index 55a9b89..0000000
--- a/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py
+++ /dev/null
@@ -1,196 +0,0 @@
-# Zxy导入Django的编码工具
-from django.utils.encoding import force_str
-# Zxy导入Elasticsearch DSL的查询构造器
-from elasticsearch_dsl import Q
-# Zxy导入Haystack的搜索后端模块
-from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query
-# Zxy导入Haystack的表单模块
-from haystack.forms import ModelSearchForm
-# Zxy导入Haystack的搜索结果模型
-from haystack.models import SearchResult
-# Zxy导入Haystack的日志工具
-from haystack.utils import log as logging
-
-# Zxy导入项目中的Elasticsearch文档和管理器
-from blog.documents import ArticleDocument, ArticleDocumentManager
-# Zxy导入项目中的文章模型
-from blog.models import Article
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy定义Elasticsearch搜索后端类
-class ElasticSearchBackend(BaseSearchBackend):
- # Zxy初始化方法
- def __init__(self, connection_alias, **connection_options):
- super(ElasticSearchBackend, self).__init__(connection_alias, **connection_options)
- self.manager = ArticleDocumentManager() # Zxy初始化文档管理器
- self.include_spelling = True # Zxy启用拼写建议
-
- # Zxy获取模型数据并转换为文档
- def _get_models(self, iterable):
- models = iterable if iterable and iterable[0] else Article.objects.all() # Zxy获取模型数据
- docs = self.manager.convert_to_doc(models) # Zxy转换为Elasticsearch文档
- return docs
-
- # Zxy创建索引并重建数据
- def _create(self, models):
- self.manager.create_index() # Zxy创建索引
- docs = self._get_models(models) # Zxy获取文档
- self.manager.rebuild(docs) # Zxy重建索引数据
-
- # Zxy删除模型数据
- def _delete(self, models):
- for m in models:
- m.delete() # Zxy删除模型实例
- return True
-
- # Zxy重建索引数据
- def _rebuild(self, models):
- models = models if models else Article.objects.all() # Zxy获取模型数据
- docs = self.manager.convert_to_doc(models) # Zxy转换为文档
- self.manager.update_docs(docs) # Zxy更新索引数据
-
- # Zxy更新索引
- def update(self, index, iterable, commit=True):
- models = self._get_models(iterable) # Zxy获取文档
- self.manager.update_docs(models) # Zxy更新索引
-
- # Zxy删除文档
- def remove(self, obj_or_string):
- models = self._get_models([obj_or_string]) # Zxy获取文档
- self._delete(models) # Zxy删除文档
-
- # Zxy清空索引
- def clear(self, models=None, commit=True):
- self.remove(None) # Zxy删除所有文档
-
- # Zxy获取拼写建议
- @staticmethod
- def get_suggestion(query: str) -> str:
- """获取推荐词,如果没有找到则返回原搜索词"""
- search = ArticleDocument.search() \
- .query("match", body=query) \
- .suggest('suggest_search', query, term={'field': 'body'}) \
- .execute()
-
- keywords = []
- for suggest in search.suggest.suggest_search:
- if suggest["options"]: # Zxy检查是否有建议选项
- keywords.append(suggest["options"][0]["text"]) # Zxy添加建议词
- else:
- keywords.append(suggest["text"]) # Zxy添加原搜索词
-
- return ' '.join(keywords) # Zxy返回拼写建议
-
- # Zxy执行搜索查询
- @log_query
- def search(self, query_string, **kwargs):
- logger.info('search query_string:' + query_string) # Zxy记录搜索查询
-
- start_offset = kwargs.get('start_offset') # Zxy获取起始偏移量
- end_offset = kwargs.get('end_offset') # Zxy获取结束偏移量
-
- # Zxy检查是否启用拼写建议
- if getattr(self, "is_suggest", None):
- suggestion = self.get_suggestion(query_string) # Zxy获取拼写建议
- else:
- suggestion = query_string # Zxy使用原搜索词
-
- q = Q('bool', # Zxy构造布尔查询
- should=[Q('match', body=suggestion), Q('match', title=suggestion)], # Zxy匹配标题或正文
- minimum_should_match="70%") # Zxy至少匹配70%
-
- search = ArticleDocument.search() \
- .query('bool', filter=[q]) \
- .filter('term', status='p') \
- .filter('term', type='a') \
- .source(False)[start_offset: end_offset] # Zxy执行搜索
-
- results = search.execute() # Zxy执行搜索查询
- hits = results['hits'].total # Zxy获取总匹配数
- raw_results = [] # Zxy初始化结果列表
-
- for raw_result in results['hits']['hits']: # Zxy遍历搜索结果
- app_label = 'blog' # Zxy应用标签
- model_name = 'Article' # Zxy模型名称
- additional_fields = {} # Zxy额外字段
-
- result_class = SearchResult # Zxy搜索结果类
-
- result = result_class( # Zxy创建搜索结果对象
- app_label,
- model_name,
- raw_result['_id'],
- raw_result['_score'],
- **additional_fields
- )
- raw_results.append(result) # Zxy添加到结果列表
-
- facets = {} # Zxy初始化分面信息
- spelling_suggestion = None if query_string == suggestion else suggestion # Zxy拼写建议
-
- return {
- 'results': raw_results, # Zxy返回搜索结果
- 'hits': hits, # Zxy返回匹配数
- 'facets': facets, # Zxy返回分面信息
- 'spelling_suggestion': spelling_suggestion, # Zxy返回拼写建议
- }
-
-# Zxy定义Elasticsearch搜索查询类
-class ElasticSearchQuery(BaseSearchQuery):
- # Zxy转换日期时间格式
- def _convert_datetime(self, date):
- if hasattr(date, 'hour'):
- return force_str(date.strftime('%Y%m%d%H%M%S'))
- else:
- return force_str(date.strftime('%Y%m%d000000'))
-
- # Zxy清理用户输入
- def clean(self, query_fragment):
- words = query_fragment.split()
- cleaned_words = []
-
- for word in words:
- if word in self.backend.RESERVED_WORDS:
- word = word.replace(word, word.lower())
-
- for char in self.backend.RESERVED_CHARACTERS:
- if char in word:
- word = "'%s'" % word
- break
-
- cleaned_words.append(word)
-
- return ' '.join(cleaned_words)
-
- # Zxy构建查询片段
- def build_query_fragment(self, field, filter_type, value):
- return value.query_string
-
- # Zxy获取结果数量
- def get_count(self):
- results = self.get_results()
- return len(results) if results else 0
-
- # Zxy获取拼写建议
- def get_spelling_suggestion(self, preferred_query=None):
- return self._spelling_suggestion
-
- # Zxy构建查询参数
- def build_params(self, spelling_query=None):
- kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query)
- return kwargs
-
-# Zxy定义Elasticsearch模型搜索表单
-class ElasticSearchModelSearchForm(ModelSearchForm):
- # Zxy执行搜索
- def search(self):
- self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no" # Zxy检查是否启用拼写建议
- sqs = super().search() # Zxy调用父类搜索方法
- return sqs
-
-# Zxy定义Elasticsearch搜索引擎
-class ElasticSearchEngine(BaseEngine):
- backend = ElasticSearchBackend # Zxy后端类
- query = ElasticSearchQuery # Zxy查询类
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/feeds.py b/src/DjangoBlog-master/djangoblog/feeds.py
deleted file mode 100644
index c7afbe3..0000000
--- a/src/DjangoBlog-master/djangoblog/feeds.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Zxy导入Django的用户模型
-from django.contrib.auth import get_user_model
-# Zxy导入Django的Feed视图
-from django.contrib.syndication.views import Feed
-# Zxy导入Django的时区工具
-from django.utils import timezone
-# Zxy导入RSS 2.0 Feed生成器
-from django.utils.feedgenerator import Rss201rev2Feed
-
-# Zxy导入项目中的文章模型
-from blog.models import Article
-# Zxy导入Markdown工具
-from djangoblog.utils import CommonMarkdown
-
-# Zxy定义Django博客Feed
-class DjangoBlogFeed(Feed):
- feed_type = Rss201rev2Feed # Zxy使用RSS 2.0格式
-
- description = '大巧无工,重剑无锋.' # ZxyFeed描述
- title = "且听风吟 大巧无工,重剑无锋." # ZxyFeed标题
- link = "/feed/" # ZxyFeed链接
-
- # Zxy获取作者名称
- def author_name(self):
- return get_user_model().objects.first().nickname
-
- # Zxy获取作者链接
- def author_link(self):
- return get_user_model().objects.first().get_absolute_url()
-
- # Zxy获取Feed项
- def items(self):
- return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5] # Zxy获取最近5篇已发布的文章
-
- # Zxy获取Feed项标题
- def item_title(self, item):
- return item.title
-
- # Zxy获取Feed项描述
- def item_description(self, item):
- return CommonMarkdown.get_markdown(item.body) # Zxy将文章内容转换为Markdown格式
-
- # Zxy获取Feed版权信息
- def feed_copyright(self):
- now = timezone.now()
- return "Copyright© {year} 且听风吟".format(year=now.year) # Zxy动态生成版权年份
-
- # Zxy获取Feed项链接
- def item_link(self, item):
- return item.get_absolute_url()
-
- # Zxy获取Feed项GUID
- def item_guid(self, item):
- return
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/logentryadmin.py b/src/DjangoBlog-master/djangoblog/logentryadmin.py
deleted file mode 100644
index e1b4bd3..0000000
--- a/src/DjangoBlog-master/djangoblog/logentryadmin.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Zxy导入Django的admin模块
-from django.contrib import admin
-# Zxy导入Django的LogEntry模型
-from django.contrib.admin.models import DELETION
-# Zxy导入Django的内容类型模型
-from django.contrib.contenttypes.models import ContentType
-# Zxy导入Django的URL工具
-from django.urls import reverse, NoReverseMatch
-# Zxy导入Django的编码工具
-from django.utils.encoding import force_str
-# Zxy导入Django的HTML工具
-from django.utils.html import escape
-# Zxy导入Django的安全字符串工具
-from django.utils.safestring import mark_safe
-# Zxy导入Django的翻译工具
-from django.utils.translation import gettext_lazy as _
-
-# Zxy定义LogEntryAdmin类
-class LogEntryAdmin(admin.ModelAdmin):
- list_filter = ['content_type'] # Zxy按内容类型过滤
- search_fields = ['object_repr', 'change_message'] # Zxy搜索字段
-
- list_display_links = ['action_time', 'get_change_message'] # Zxy显示链接的字段
- list_display = ['action_time', 'user_link', 'content_type', 'object_link', 'get_change_message'] # Zxy显示字段
-
- # Zxy检查是否有添加权限
- def has_add_permission(self, request):
- return False
-
- # Zxy检查是否有修改权限
- def has_change_permission(self, request, obj=None):
- return (
- request.user.is_superuser or
- request.user.has_perm('admin.change_logentry')
- ) and request.method != 'POST'
-
- # Zxy检查是否有删除权限
- def has_delete_permission(self, request, obj=None):
- return False
-
- # Zxy获取对象链接
- def object_link(self, obj):
- object_link = escape(obj.object_repr) # Zxy转义对象表示
- content_type = obj.content_type
-
- if obj.action_flag != DELETION and content_type is not None: # Zxy检查是否为删除操作
- try:
- url = reverse( # Zxy生成反向URL
- 'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
- args=[obj.object_id]
- )
- object_link = '{}'.format(url, object_link) # Zxy生成链接
- except NoReverseMatch:
- pass
- return mark_safe(object_link) # Zxy标记为安全字符串
-
- object_link.admin_order_field = 'object_repr' # Zxy排序字段
- object_link.short_description = _('object') # Zxy字段描述
-
- # Zxy获取用户链接
- def user_link(self, obj):
- content_type = ContentType.objects.get_for_model(type(obj.user)) # Zxy获取用户的内容类型
- user_link = escape(force_str(obj.user)) # Zxy转义用户表示
-
- try:
- url = reverse( # Zxy生成反向URL
- 'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
- args=[obj.user.pk]
- )
- user_link = '{}'.format(url, user_link) # Zxy生成链接
- except NoReverseMatch:
- pass
- return mark_safe(user_link) # Zxy标记为安全字符串
-
- user_link.admin_order_field = 'user' # Zxy排序字段
- user_link.short_description = _('user') # Zxy字段描述
-
- # Zxy获取查询集
- def get_queryset(self, request):
- queryset = super(LogEntryAdmin, self).get_queryset(request)
- return queryset.prefetch_related('content_type') # Zxy预加载内容类型
-
- # Zxy获取操作
- def get_actions(self, request):
- actions = super(LogEntryAdmin, self).get_actions(request)
- if 'delete_selected' in actions: # Zxy移除删除操作
- del actions['delete_selected']
- return actions
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py b/src/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py
deleted file mode 100644
index e87d7df..0000000
--- a/src/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Zxy导入日志模块
-import logging
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy定义基础插件类
-class BasePlugin:
- # Zxy插件元数据
- PLUGIN_NAME = None # Zxy插件名称
- PLUGIN_DESCRIPTION = None # Zxy插件描述
- PLUGIN_VERSION = None # Zxy插件版本
-
- # Zxy初始化插件
- def __init__(self):
- # Zxy检查插件元数据是否已定义
- 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() # Zxy调用初始化方法
- self.register_hooks() # Zxy注册钩子
-
- # Zxy插件初始化逻辑
- def init_plugin(self):
- """
- 插件初始化逻辑
- 子类可以重写此方法来实现特定的初始化操作
- """
- logger.info(f'{self.PLUGIN_NAME} initialized.') # Zxy记录插件初始化日志
-
- # Zxy注册插件钩子
- def register_hooks(self):
- """
- 注册插件钩子
- 子类可以重写此方法来注册特定的钩子
- """
- pass
-
- # Zxy获取插件信息
- def get_plugin_info(self):
- """
- 获取插件信息
- :return: 包含插件元数据的字典
- """
- return {
- 'name': self.PLUGIN_NAME, # Zxy插件名称
- 'description': self.PLUGIN_DESCRIPTION, # Zxy插件描述
- 'version': self.PLUGIN_VERSION # Zxy插件版本
- }
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py b/src/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py
deleted file mode 100644
index cabaed1..0000000
--- a/src/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Zxy定义文章相关的钩子常量
-ARTICLE_DETAIL_LOAD = 'article_detail_load' # Zxy文章详情加载时触发的钩子
-ARTICLE_CREATE = 'article_create' # Zxy文章创建时触发的钩子
-ARTICLE_UPDATE = 'article_update' # Zxy文章更新时触发的钩子
-ARTICLE_DELETE = 'article_delete' # Zxy文章删除时触发的钩子
-
-# Zxy定义文章内容处理的钩子名称
-ARTICLE_CONTENT_HOOK_NAME = "the_content" # Zxy文章内容处理的钩子名称
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/plugin_manage/hooks.py b/src/DjangoBlog-master/djangoblog/plugin_manage/hooks.py
deleted file mode 100644
index 75317ab..0000000
--- a/src/DjangoBlog-master/djangoblog/plugin_manage/hooks.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Zxy导入日志模块
-import logging
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy定义全局钩子字典
-_hooks = {}
-
-# Zxy注册钩子回调
-def register(hook_name: str, callback: callable):
- """
- 注册一个钩子回调。
- """
- if hook_name not in _hooks: # Zxy检查钩子是否已存在
- _hooks[hook_name] = [] # Zxy初始化钩子列表
- _hooks[hook_name].append(callback) # Zxy添加回调到钩子列表
- logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'") # Zxy记录注册日志
-
-# Zxy执行Action Hook
-def run_action(hook_name: str, *args, **kwargs):
- """
- 执行一个 Action Hook。
- 它会按顺序执行所有注册到该钩子上的回调函数。
- """
- if hook_name in _hooks: # Zxy检查钩子是否存在
- logger.debug(f"Running action hook '{hook_name}'") # Zxy记录执行日志
- for callback in _hooks[hook_name]: # Zxy遍历钩子回调
- try:
- callback(*args, **kwargs) # Zxy执行回调
- except Exception as e:
- logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) # Zxy记录错误日志
-
-# Zxy执行Filter Hook
-def apply_filters(hook_name: str, value, *args, **kwargs):
- """
- 执行一个 Filter Hook。
- 它会把 value 依次传递给所有注册的回调函数进行处理。
- """
- if hook_name in _hooks: # Zxy检查钩子是否存在
- logger.debug(f"Applying filter hook '{hook_name}'") # Zxy记录执行日志
- for callback in _hooks[hook_name]: # Zxy遍历钩子回调
- try:
- value = callback(value, *args, **kwargs) # Zxy调用回调处理值
- except Exception as e:
- logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) # Zxy记录错误日志
- return value # Zxy返回处理后的值
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/plugin_manage/loader.py b/src/DjangoBlog-master/djangoblog/plugin_manage/loader.py
deleted file mode 100644
index 9ee7372..0000000
--- a/src/DjangoBlog-master/djangoblog/plugin_manage/loader.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Zxy导入os模块,用于操作路径
-import os
-# Zxy导入日志模块
-import logging
-# Zxy导入Django的配置模块
-from django.conf import settings
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy动态加载插件
-def load_plugins():
- """
- Dynamically loads and initializes plugins from the 'plugins' directory.
- This function is intended to be called when the Django app registry is ready.
- """
- for plugin_name in settings.ACTIVE_PLUGINS: # Zxy遍历激活的插件
- plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name) # Zxy获取插件路径
- if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')): # Zxy检查插件目录和文件
- try:
- __import__(f'plugins.{plugin_name}.plugin') # Zxy动态导入插件模块
- logger.info(f"Successfully loaded plugin: {plugin_name}") # Zxy记录加载成功日志
- except ImportError as e:
- logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e) # Zxy记录加载失败日志
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/settings.py b/src/DjangoBlog-master/djangoblog/settings.py
deleted file mode 100644
index 943c243..0000000
--- a/src/DjangoBlog-master/djangoblog/settings.py
+++ /dev/null
@@ -1,344 +0,0 @@
-"""
-Django settings for djangoblog project.
-
-Generated by 'django-admin startproject' using Django 1.10.2.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.10/ref/settings/
-"""
-import os
-import sys
-from pathlib import Path
-
-from django.utils.translation import gettext_lazy as _
-
-
-# Zxy环境变量布尔值转换函数:将环境变量转换为布尔值
-def env_to_bool(env, default):
- str_val = os.environ.get(env)
- return default if str_val is None else str_val == 'True'
-
-
-# Zxy项目根目录:获取项目的绝对路径
-BASE_DIR = Path(__file__).resolve().parent.parent
-
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = os.environ.get(
- 'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6'
-# Zxy调试模式:从环境变量获取或默认为 True
-DEBUG = env_to_bool('DJANGO_DEBUG', True)
-# Zxy测试模式检测:检查是否运行测试
-TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
-
-# Zxy允许的主机名:允许访问的域名列表
-ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']
-# ZxyCSRF 可信来源:允许的 CSRF 来源
-CSRF_TRUSTED_ORIGINS = ['http://example.com']
-# Application definition
-
-# Zxy应用程序定义:定义项目中使用的 Django 应用程序
-INSTALLED_APPS = [
- # 默认的 Django 应用程序
- 'django.contrib.admin.apps.SimpleAdminConfig',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'django.contrib.sites',
- 'django.contrib.sitemaps',
- # 第三方和自定义应用程序
- 'mdeditor',
- 'haystack',
- 'blog',
- 'accounts',
- 'comments',
- 'oauth',
- 'servermanager',
- 'owntracks',
- 'compressor',
- 'djangoblog'
-]
-# Zxy中间件配置:定义请求处理的中间件
-MIDDLEWARE = [
-
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.locale.LocaleMiddleware',
- 'django.middleware.gzip.GZipMiddleware',
- # 'django.middleware.cache.UpdateCacheMiddleware',
- 'django.middleware.common.CommonMiddleware',
- # 'django.middleware.cache.FetchFromCacheMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'django.middleware.http.ConditionalGetMiddleware',
- 'blog.middleware.OnlineMiddleware'
-]
-# ZxyURL 配置:定义项目的 URL 配置文件
-ROOT_URLCONF = 'djangoblog.urls'
-# Zxy模板配置:配置 Django 模板引擎
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR, 'templates')],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- 'blog.context_processors.seo_processor'
- ],
- },
- },
-]
-# ZxyWSGI 应用程序:定义 WSGI 应用程序入口
-WSGI_APPLICATION = 'djangoblog.wsgi.application'
-
-# Database
-# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
-
-# Zxy数据库配置:配置数据库连接
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'djangoblog',
- 'USER': 'root',
- 'PASSWORD': '123456',
- 'HOST': '127.0.0.1',
- 'PORT': 3306,
- }
-}
-
-# Password validation
-# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
-# Zxy密码验证器:配置密码验证规则
-AUTH_PASSWORD_VALIDATORS = [
- {
- 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
- },
-]
-# Zxy语言和时区配置:配置语言和时区
-LANGUAGES = (
- ('en', _('English')),
- ('zh-hans', _('Simplified Chinese')),
- ('zh-hant', _('Traditional Chinese')),
-)
-LOCALE_PATHS = (
- os.path.join(BASE_DIR, 'locale'),
-)
-
-LANGUAGE_CODE = 'zh-hans'
-
-TIME_ZONE = 'Asia/Shanghai'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = False
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.10/howto/static-files/
-
-# ZxyHaystack 搜索配置:配置 Haystack 搜索引擎
-HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine',
- 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
- },
-}
-# Automatically update searching index
-HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
-# Allow user login with username and password
-AUTHENTICATION_BACKENDS = [
- 'accounts.user_login_backend.EmailOrUsernameModelBackend']
-# Zxy静态文件配置:配置静态文件路径
-STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic')
-
-STATIC_URL = '/static/'
-STATICFILES = os.path.join(BASE_DIR, 'static')
-# Zxy用户模型配置:定义自定义用户模型和登录 URL
-AUTH_USER_MODEL = 'accounts.BlogUser'
-LOGIN_URL = '/login/'
-
-TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
-DATE_TIME_FORMAT = '%Y-%m-%d'
-
-# bootstrap color styles
-BOOTSTRAP_COLOR_TYPES = [
- 'default', 'primary', 'success', 'info', 'warning', 'danger'
-]
-
-# paginate
-PAGINATE_BY = 10
-# http cache timeout
-CACHE_CONTROL_MAX_AGE = 2592000
-# cache setting
-# Zxy缓存配置:配置缓存后端
-CACHES = {
- 'default': {
- 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
- 'TIMEOUT': 10800,
- 'LOCATION': 'unique-snowflake',
- }
-}
-# 使用redis作为缓存
-if os.environ.get("DJANGO_REDIS_URL"):
- CACHES = {
- 'default': {
- 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
- 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}',
- }
- }
-# Zxy站点 ID:定义当前站点的 ID
-SITE_ID = 1
-BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_URL') \
- or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn'
-
-# Email:
-EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
-EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False)
-EMAIL_USE_SSL = env_to_bool('DJANGO_EMAIL_SSL', True)
-EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST') or 'smtp.mxhichina.com'
-EMAIL_PORT = int(os.environ.get('DJANGO_EMAIL_PORT') or 465)
-EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER')
-EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD')
-DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
-SERVER_EMAIL = EMAIL_HOST_USER
-# Setting debug=false did NOT handle except email notifications
-ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')]
-# WX ADMIN password(Two times md5)
-WXADMIN = os.environ.get(
- 'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7'
-
-LOG_PATH = os.path.join(BASE_DIR, 'logs')
-if not os.path.exists(LOG_PATH):
- os.makedirs(LOG_PATH, exist_ok=True)
-# Zxy日志配置:配置日志记录
-LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'root': {
- 'level': 'INFO',
- 'handlers': ['console', 'log_file'],
- },
- 'formatters': {
- 'verbose': {
- 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d %(module)s] %(message)s',
- }
- },
- 'filters': {
- 'require_debug_false': {
- '()': 'django.utils.log.RequireDebugFalse',
- },
- 'require_debug_true': {
- '()': 'django.utils.log.RequireDebugTrue',
- },
- },
- 'handlers': {
- 'log_file': {
- 'level': 'INFO',
- 'class': 'logging.handlers.TimedRotatingFileHandler',
- 'filename': os.path.join(LOG_PATH, 'djangoblog.log'),
- 'when': 'D',
- 'formatter': 'verbose',
- 'interval': 1,
- 'delay': True,
- 'backupCount': 5,
- 'encoding': 'utf-8'
- },
- 'console': {
- 'level': 'DEBUG',
- 'filters': ['require_debug_true'],
- 'class': 'logging.StreamHandler',
- 'formatter': 'verbose'
- },
- 'null': {
- 'class': 'logging.NullHandler',
- },
- 'mail_admins': {
- 'level': 'ERROR',
- 'filters': ['require_debug_false'],
- 'class': 'django.utils.log.AdminEmailHandler'
- }
- },
- 'loggers': {
- 'djangoblog': {
- 'handlers': ['log_file', 'console'],
- 'level': 'INFO',
- 'propagate': True,
- },
- 'django.request': {
- 'handlers': ['mail_admins'],
- 'level': 'ERROR',
- 'propagate': False,
- }
- }
-}
-# Zxy压缩配置:配置静态文件压缩
-STATICFILES_FINDERS = (
- 'django.contrib.staticfiles.finders.FileSystemFinder',
- 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
- # other
- 'compressor.finders.CompressorFinder',
-)
-COMPRESS_ENABLED = True
-# COMPRESS_OFFLINE = True
-
-
-COMPRESS_CSS_FILTERS = [
- # creates absolute urls from relative ones
- 'compressor.filters.css_default.CssAbsoluteFilter',
- # css minimizer
- 'compressor.filters.cssmin.CSSMinFilter'
-]
-COMPRESS_JS_FILTERS = [
- 'compressor.filters.jsmin.JSMinFilter'
-]
-# Zxy媒体文件配置:配置媒体文件路径
-MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
-MEDIA_URL = '/media/'
-X_FRAME_OPTIONS = 'SAMEORIGIN'
-
-DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
-# ZxyElasticsearch 配置:配置 Elasticsearch 搜索引擎
-if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
- ELASTICSEARCH_DSL = {
- 'default': {
- 'hosts': os.environ.get('DJANGO_ELASTICSEARCH_HOST')
- },
- }
- HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'djangoblog.elasticsearch_backend.ElasticSearchEngine',
- },
- }
-
-# Zxy插件系统配置:定义插件目录和激活的插件
-PLUGINS_DIR = BASE_DIR / 'plugins'
-ACTIVE_PLUGINS = [
- 'article_copyright',
- 'reading_time',
- 'external_links',
- 'view_count',
- 'seo_optimizer'
-]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/sitemap.py b/src/DjangoBlog-master/djangoblog/sitemap.py
deleted file mode 100644
index 986f1ef..0000000
--- a/src/DjangoBlog-master/djangoblog/sitemap.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Zxy导入 Django 的 Sitemap 模块和其他相关模块
-from django.contrib.sitemaps import Sitemap
-from django.urls import reverse
-
-from blog.models import Article, Category, Tag
-
-# Zxy定义静态页面的 Sitemap
-class StaticViewSitemap(Sitemap):
- priority = 0.5 # Zxy设置优先级
- changefreq = 'daily' # Zxy设置更新频率
-
- def items(self):
- # Zxy返回静态页面的 URL 名称
- return ['blog:index', ]
-
- def location(self, item):
- # Zxy通过 URL 名称生成完整的 URL
- return reverse(item)
-
-# Zxy定义文章的 Sitemap
-class ArticleSiteMap(Sitemap):
- changefreq = "monthly" # Zxy文章更新频率为每月
- priority = "0.6" # Zxy文章优先级
-
- def items(self):
- # Zxy返回所有已发布的文章
- return Article.objects.filter(status='p')
-
- def lastmod(self, obj):
- # Zxy返回文章的最后修改时间
- return obj.last_modify_time
-
-# Zxy定义分类的 Sitemap
-class CategorySiteMap(Sitemap):
- changefreq = "Weekly" # Zxy分类更新频率为每周
- priority = "0.6" # Zxy分类优先级
-
- def items(self):
- # Zxy返回所有分类
- return Category.objects.all()
-
- def lastmod(self, obj):
- # Zxy返回分类的最后修改时间
- return obj.last_modify_time
-
-# Zxy定义标签的 Sitemap
-class TagSiteMap(Sitemap):
- changefreq = "Weekly" # Zxy标签更新频率为每周
- priority = "0.3" # Zxy标签优先级
-
- def items(self):
- # Zxy返回所有标签
- return Tag.objects.all()
-
- def lastmod(self, obj):
- # Zxy返回标签的最后修改时间
- return obj.last_modify_time
-
-# Zxy定义用户的 Sitemap
-class UserSiteMap(Sitemap):
- changefreq = "Weekly" # Zxy用户更新频率为每周
- priority = "0.3" # Zxy用户优先级
-
- def items(self):
- # Zxy返回所有用户的作者列表(去重)
- return list(set(map(lambda x: x.author, Article.objects.all())))
-
- def lastmod(self, obj):
- # Zxy返回用户的注册时间
- return obj.date_joined
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/spider_notify.py b/src/DjangoBlog-master/djangoblog/spider_notify.py
deleted file mode 100644
index 20fed14..0000000
--- a/src/DjangoBlog-master/djangoblog/spider_notify.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Zxy导入日志模块
-import logging
-
-# Zxy导入 requests 模块用于发送 HTTP 请求
-import requests
-from django.conf import settings
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy定义爬虫通知类
-class SpiderNotify():
- @staticmethod
- def baidu_notify(urls):
- # Zxy向百度站长平台发送 URL 提交请求
- try:
- data = '\n'.join(urls) # Zxy将 URL 列表拼接为字符串
- result = requests.post(settings.BAIDU_NOTIFY_URL, data=data) # Zxy发送 POST 请求
- logger.info(result.text) # Zxy记录响应内容
- except Exception as e:
- logger.error(e) # Zxy记录异常信息
-
- @staticmethod
- def notify(url):
- # Zxy调用百度通知方法
- SpiderNotify.baidu_notify(url)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/tests.py b/src/DjangoBlog-master/djangoblog/tests.py
deleted file mode 100644
index 83af4c1..0000000
--- a/src/DjangoBlog-master/djangoblog/tests.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Zxy导入 Django 的测试模块
-from django.test import TestCase
-
-# Zxy导入项目中的工具函数
-from djangoblog.utils import *
-
-# Zxy定义测试类
-class DjangoBlogTest(TestCase):
- def setUp(self):
- # Zxy测试初始化方法(暂无内容)
- pass
-
- def test_utils(self):
- # Zxy测试工具函数
- md5 = get_sha256('test') # Zxy测试 SHA256 函数
- self.assertIsNotNone(md5) # Zxy断言返回值不为空
-
- c = CommonMarkdown.get_markdown(''' # Zxy测试 Markdown 转换
- # Title1
-
- ```python
- import os
- ```
-
- [url](https://www.lylinux.net/)
-
- [ddd](http://www.baidu.com)
-
-
- ''')
- self.assertIsNotNone(c) # Zxy断言返回值不为空
-
- d = {
- 'd': 'key1',
- 'd2': 'key2'
- }
- data = parse_dict_to_url(d) # Zxy测试字典转 URL 函数
- self.assertIsNotNone(data) # Zxy断言返回值不为空
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/urls.py b/src/DjangoBlog-master/djangoblog/urls.py
deleted file mode 100644
index f158317..0000000
--- a/src/DjangoBlog-master/djangoblog/urls.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Zxy定义项目的 URL 配置
-"""djangoblog URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/1.10/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.conf.urls import url, include
- 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
-"""
-from django.conf import settings
-from django.conf.urls.i18n import i18n_patterns
-from django.conf.urls.static import static
-from django.contrib.sitemaps.views import sitemap
-from django.urls import path, include
-from django.urls import re_path
-from haystack.views import search_view_factory
-
-from blog.views import EsSearchView
-from djangoblog.admin_site import admin_site
-from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm
-from djangoblog.feeds import DjangoBlogFeed
-from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap
-
-# Zxy定义站点地图
-sitemaps = {
- 'blog': ArticleSiteMap,
- 'Category': CategorySiteMap,
- 'Tag': TagSiteMap,
- 'User': UserSiteMap,
- 'static': StaticViewSitemap
-}
-
-# Zxy定义 404、500 和 403 错误页面的视图
-handler404 = 'blog.views.page_not_found_view'
-handler500 = 'blog.views.server_error_view'
-handle403 = 'blog.views.permission_denied_view'
-
-# Zxy定义 URL 模式
-urlpatterns = [
- path('i18n/', include('django.conf.urls.i18n')), # Zxy国际化语言切换
-]
-urlpatterns += i18n_patterns(
- re_path(r'^admin/', admin_site.urls), # Zxy自定义 Admin 站点
- re_path(r'', include('blog.urls', namespace='blog')), # Zxy博客应用的 URL
- re_path(r'mdeditor/', include('mdeditor.urls')), # Zxy Markdown 编辑器的 URL
- re_path(r'', include('comments.urls', namespace='comment')), # Zxy评论应用的 URL
- re_path(r'', include('accounts.urls', namespace='account')), # Zxy用户账户的 URL
- re_path(r'', include('oauth.urls', namespace='oauth')), # Zxy OAuth 应用的 URL
- re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, # Zxy站点地图
- name='django.contrib.sitemaps.views.sitemap'),
- re_path(r'^feed/$', DjangoBlogFeed()), # Zxy RSS 订阅
- re_path(r'^rss/$', DjangoBlogFeed()), # Zxy RSS 订阅
- re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm), # Zxy搜索功能
- name='search'),
- re_path(r'', include('servermanager.urls', namespace='servermanager')), # Zxy服务器管理应用的 URL
- re_path(r'', include('owntracks.urls', namespace='owntracks')), # Zxy位置跟踪应用的 URL
- prefix_default_language=False
-) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) # Zxy静态文件的 URL
-if settings.DEBUG:
- urlpatterns += static(settings.MEDIA_URL, # Zxy媒体文件的 URL
- document_root=settings.MEDIA_ROOT)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/utils.py b/src/DjangoBlog-master/djangoblog/utils.py
deleted file mode 100644
index 90cfa42..0000000
--- a/src/DjangoBlog-master/djangoblog/utils.py
+++ /dev/null
@@ -1,198 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-
-# Zxy导入日志模块
-import logging
-import os
-import random
-import string
-import uuid
-from hashlib import sha256
-
-import bleach
-import markdown
-import requests
-from django.conf import settings
-from django.contrib.sites.models import Site
-from django.core.cache import cache
-from django.templatetags.static import static
-
-# Zxy获取日志记录器
-logger = logging.getLogger(__name__)
-
-# Zxy获取文章和评论的最大 ID
-def get_max_articleid_commentid():
- from blog.models import Article
- from comments.models import Comment
- return (Article.objects.latest().pk, Comment.objects.latest().pk)
-
-# Zxy计算字符串的 SHA256 值
-def get_sha256(str):
- m = sha256(str.encode('utf-8'))
- return m.hexdigest()
-
-# Zxy缓存装饰器,用于缓存函数的返回值
-def cache_decorator(expiration=3 * 60):
- def wrapper(func):
- def news(*args, **kwargs):
- try:
- view = args[0]
- key = view.get_cache_key()
- except:
- key = None
- if not key:
- unique_str = repr((func, args, kwargs))
- m = sha256(unique_str.encode('utf-8'))
- key = m.hexdigest()
- value = cache.get(key)
- if value is not None:
- if str(value) == '__default_cache_value__':
- return None
- else:
- return value
- else:
- logger.debug('cache_decorator set cache:%s key:%s' % (func.__name__, key))
- value = func(*args, **kwargs)
- if value is None:
- cache.set(key, '__default_cache_value__', expiration)
- else:
- cache.set(key, value, expiration)
- return value
- return news
- return wrapper
-
-# Zxy刷新视图缓存
-def expire_view_cache(path, servername, serverport, key_prefix=None):
- from django.http import HttpRequest
- from django.utils.cache import get_cache_key
-
- request = HttpRequest()
- request.META = {'SERVER_NAME': servername, 'SERVER_PORT': serverport}
- request.path = path
-
- key = get_cache_key(request, key_prefix=key_prefix, cache=cache)
- if key:
- logger.info('expire_view_cache:get key:{path}'.format(path=path))
- if cache.get(key):
- cache.delete(key)
- return True
- return False
-
-# Zxy获取当前站点
-@cache_decorator()
-def get_current_site():
- site = Site.objects.get_current()
- return site
-
-# Zxy定义 CommonMarkdown 类,用于处理 Markdown 转换
-class CommonMarkdown:
- @staticmethod
- def _convert_markdown(value):
- md = markdown.Markdown(extensions=['extra', 'codehilite', 'toc', 'tables'])
- body = md.convert(value)
- toc = md.toc
- return body, toc
-
- @staticmethod
- def get_markdown_with_toc(value):
- body, toc = CommonMarkdown._convert_markdown(value)
- return body, toc
-
- @staticmethod
- def get_markdown(value):
- body, toc = CommonMarkdown._convert_markdown(value)
- return body
-
-# Zxy发送邮件
-def send_email(emailto, title, content):
- from djangoblog.blog_signals import send_email_signal
- send_email_signal.send(send_email.__class__, emailto=emailto, title=title, content=content)
-
-# Zxy生成随机验证码
-def generate_code() -> str:
- return ''.join(random.sample(string.digits, 6))
-
-# Zxy将字典转换为 URL 参数
-def parse_dict_to_url(dict):
- from urllib.parse import quote
- url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/')) for k, v in dict.items()])
- return url
-
-# Zxy获取博客设置
-def get_blog_setting():
- value = cache.get('get_blog_setting')
- if value:
- return value
- else:
- from blog.models import BlogSettings
- if not BlogSettings.objects.count():
- setting = BlogSettings()
- setting.site_name = 'djangoblog'
- setting.site_description = '基于Django的博客系统'
- setting.site_seo_description = '基于Django的博客系统'
- setting.site_keywords = 'Django,Python'
- setting.article_sub_length = 300
- setting.sidebar_article_count = 10
- setting.sidebar_comment_count = 5
- setting.show_google_adsense = False
- setting.open_site_comment = True
- setting.analytics_code = ''
- setting.beian_code = ''
- setting.show_gongan_code = False
- setting.comment_need_review = False
- setting.save()
- value = BlogSettings.objects.first()
- logger.info('set cache get_blog_setting')
- cache.set('get_blog_setting', value)
- return value
-
-# Zxy保存用户头像
-def save_user_avatar(url):
- logger.info(url)
- try:
- basedir = os.path.join(settings.STATICFILES, 'avatar')
- rsp = requests.get(url, timeout=2)
- if rsp.status_code == 200:
- if not os.path.exists(basedir):
- os.makedirs(basedir)
- image_extensions = ['.jpg', '.png', 'jpeg', '.gif']
- isimage = len([i for i in image_extensions if url.endswith(i)]) > 0
- ext = os.path.splitext(url)[1] if isimage else '.jpg'
- save_filename = str(uuid.uuid4().hex) + ext
- logger.info('保存用户头像:' + basedir + save_filename)
- with open(os.path.join(basedir, save_filename), 'wb+') as file:
- file.write(rsp.content)
- return static('avatar/' + save_filename)
- except Exception as e:
- logger.error(e)
- return static('blog/img/avatar.png')
-
-# Zxy删除侧边栏缓存
-def delete_sidebar_cache():
- 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)
-
-# Zxy删除视图缓存
-def delete_view_cache(prefix, keys):
- from django.core.cache.utils import make_template_fragment_key
- key = make_template_fragment_key(prefix, keys)
- cache.delete(key)
-
-# Zxy获取资源 URL
-def get_resource_url():
- if settings.STATIC_URL:
- return settings.STATIC_URL
- else:
- site = get_current_site()
- return 'http://' + site.domain + '/static/'
-
-# Zxy定义允许的 HTML 标签和属性
-ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1', 'h2', 'p']
-ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
-
-# Zxy清理 HTML 内容
-def sanitize_html(html):
- return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/whoosh_cn_backend.py b/src/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
deleted file mode 100644
index 994247c..0000000
--- a/src/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
+++ /dev/null
@@ -1,1256 +0,0 @@
-# Zxy 导入未来支持模块,确保代码在 Python 2 和 3 中的兼容性
-# encoding: utf-8
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-# Zxy 导入标准库模块
-import json
-import os
-import re
-import shutil
-import threading
-import warnings
-
-# Zxy 导入第三方库
-import six
-from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
-from datetime import datetime
-from django.utils.encoding import force_str
-from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, EmptyResults, log_query
-from haystack.constants import DJANGO_CT, DJANGO_ID, ID
-from haystack.exceptions import MissingDependency, SearchBackendError, SkipDocument
-from haystack.inputs import Clean, Exact, PythonData, Raw
-from haystack.models import SearchResult
-from haystack.utils import get_identifier, get_model_ct
-from haystack.utils import log as logging
-from haystack.utils.app_loading import haystack_get_model
-from jieba.analyse import ChineseAnalyzer
-from whoosh import index
-from whoosh.analysis import StemmingAnalyzer
-from whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXT
-from whoosh.fields import ID as WHOOSH_ID
-from whoosh.filedb.filestore import FileStorage, RamStorage
-from whoosh.highlight import ContextFragmenter, HtmlFormatter
-from whoosh.highlight import highlight as whoosh_highlight
-from whoosh.qparser import QueryParser
-from whoosh.searching import ResultsPage
-from whoosh.writing import AsyncWriter
-
-# Zxy 尝试导入 whoosh 库,如果失败则抛出依赖缺失异常
-try:
- import whoosh
-except ImportError:
- raise MissingDependency(
- "The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.")
-
-# Zxy 检查 whoosh 版本是否满足最低要求
-if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):
- raise MissingDependency(
- "The 'whoosh' backend requires version 2.5.0 or greater.")
-
-# Zxy 定义一个正则表达式,用于匹配 ISO 8601 格式的日期时间字符串
-DATETIME_REGEX = re.compile(
- '^(?P\d{4})-(?P\d{2})-(?P\d{2})T(?P\d{2}):(?P\d{2}):(?P\d{2})(\.\d{3,6}Z?)?$')
-# Zxy 创建一个线程局部存储对象,用于在每个线程中存储独立的数据
-LOCALS = threading.local()
-# Zxy 初始化线程局部存储中的 RAM_STORE 为 None,用于存储内存索引
-LOCALS.RAM_STORE = None
-
-
-# Zxy 定义一个自定义的 HTML 格式化器,继承自 Whoosh 的 HtmlFormatter
-class WhooshHtmlFormatter(HtmlFormatter):
- """
- This is a HtmlFormatter simpler than the whoosh.HtmlFormatter.
- We use it to have consistent results across backends. Specifically,
- Solr, Xapian and Elasticsearch are using this formatting.
- """
- # Zxy 定义高亮显示的 HTML 模板
- template = '<%(tag)s>%(t)s%(tag)s>'
-
-
-# Zxy 定义 Whoosh 搜索后端类,继承自 Haystack 的 BaseSearchBackend
-class WhooshSearchBackend(BaseSearchBackend):
- # Zxy 定义 Whoosh 保留的关键字列表,这些关键字在查询中有特殊含义
- RESERVED_WORDS = (
- 'AND',
- 'NOT',
- 'OR',
- 'TO',
- )
-
- # Zxy 定义 Whoosh 保留的字符列表,这些字符在查询中有特殊含义
- # The '\\' must come first, so as not to overwrite the other slash
- # replacements.
- RESERVED_CHARACTERS = (
- '\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}',
- '[', ']', '^', '"', '~', '*', '?', ':', '.',
- )
-
- # Zxy 初始化方法,设置连接别名和连接选项
- def __init__(self, connection_alias, **connection_options):
- # Zxy 调用父类的初始化方法
- super(
- WhooshSearchBackend,
- self).__init__(
- connection_alias,
- **connection_options)
- # Zxy 标记后端设置尚未完成
- self.setup_complete = False
- # Zxy 默认使用文件存储
- self.use_file_storage = True
- # Zxy 设置 POST 请求的大小限制,默认为 128MB
- self.post_limit = getattr(
- connection_options,
- 'POST_LIMIT',
- 128 * 1024 * 1024)
- # Zxy 从连接选项中获取索引文件存储路径
- self.path = connection_options.get('PATH')
-
- # Zxy 如果配置的存储类型不是 'file',则使用内存存储
- if connection_options.get('STORAGE', 'file') != 'file':
- self.use_file_storage = False
-
- # Zxy 如果使用文件存储但未提供路径,则抛出配置错误异常
- if self.use_file_storage and not self.path:
- raise ImproperlyConfigured(
- "You must specify a 'PATH' in your settings for connection '%s'." %
- connection_alias)
-
- # Zxy 获取 Haystack 的日志记录器
- self.log = logging.getLogger('haystack')
-
-
- # Zxy 设置搜索引擎,延迟加载直到需要时才执行
- def setup(self):
- """
- Defers loading until needed.
- """
- # Zxy 导入 haystack 的连接管理器
- from haystack import connections
- # Zxy 标记是否为新创建的索引
- new_index = False
-
- # Zxy 确保索引目录存在
- if self.use_file_storage and not os.path.exists(self.path):
- # Zxy 如果目录不存在,则创建它
- os.makedirs(self.path)
- # Zxy 标记这是一个新索引
- new_index = True
-
- # Zxy 检查索引目录是否可写
- if self.use_file_storage and not os.access(self.path, os.W_OK):
- # Zxy 如果不可写,则抛出 IO 错误
- raise IOError(
- "The path to your Whoosh index '%s' is not writable for the current user/group." %
- self.path)
-
- # Zxy 根据配置选择存储方式:文件存储或内存存储
- if self.use_file_storage:
- # Zxy 使用文件存储
- self.storage = FileStorage(self.path)
- else:
- # Zxy 声明使用全局的线程局部存储
- global LOCALS
-
- # Zxy 如果内存存储尚未初始化,则创建一个
- if getattr(LOCALS, 'RAM_STORE', None) is None:
- LOCALS.RAM_STORE = RamStorage()
-
- # Zxy 使用内存存储
- self.storage = LOCALS.RAM_STORE
-
- # Zxy 构建索引模式,并获取主内容字段名
- self.content_field_name, self.schema = self.build_schema(
- connections[self.connection_alias].get_unified_index().all_searchfields())
- # Zxy 创建查询解析器,用于解析用户查询
- self.parser = QueryParser(self.content_field_name, schema=self.schema)
-
- # Zxy 根据是否为新索引来创建或打开索引
- if new_index is True:
- # Zxy 如果是新索引,则根据 schema 创建
- self.index = self.storage.create_index(self.schema)
- else:
- # Zxy 否则尝试打开现有索引
- try:
- self.index = self.storage.open_index(schema=self.schema)
- except index.EmptyIndexError:
- # Zxy 如果索引为空(可能刚创建目录但无文件),则创建新索引
- self.index = self.storage.create_index(self.schema)
-
- # Zxy 标记后端设置已完成
- self.setup_complete = True
-
- # Zxy 根据 Haystack 的搜索字段构建 Whoosh 的索引模式
- def build_schema(self, fields):
- # Zxy 初始化模式字段,包含 Haystack 内置的 ID、类型和模型 ID
- schema_fields = {
- ID: WHOOSH_ID(stored=True, unique=True),
- DJANGO_CT: WHOOSH_ID(stored=True),
- DJANGO_ID: WHOOSH_ID(stored=True),
- }
- # Zxy 获取 Haystack 内置字段的数量,用于后续检查
- initial_key_count = len(schema_fields)
- # Zxy 初始化主内容字段名
- content_field_name = ''
-
- # Zxy 遍历所有搜索字段,根据字段类型转换为 Whoosh 字段
- for field_name, field_class in fields.items():
- # Zxy 如果字段是多值字段
- if field_class.is_multivalued:
- if field_class.indexed is False:
- # Zxy 如果多值字段不被索引,使用 IDLIST 类型
- schema_fields[field_class.index_fieldname] = IDLIST(
- stored=True, field_boost=field_class.boost)
- else:
- # Zxy 如果多值字段被索引,使用 KEYWORD 类型
- schema_fields[field_class.index_fieldname] = KEYWORD(
- stored=True, commas=True, scorable=True, field_boost=field_class.boost)
- # Zxy 如果字段类型是日期或日期时间
- elif field_class.field_type in ['date', 'datetime']:
- # Zxy 使用 DATETIME 类型,并设置为可排序
- schema_fields[field_class.index_fieldname] = DATETIME(
- stored=field_class.stored, sortable=True)
- # Zxy 如果字段类型是整数
- elif field_class.field_type == 'integer':
- # Zxy 使用 NUMERIC 类型,并指定数字类型为整数
- schema_fields[field_class.index_fieldname] = NUMERIC(
- stored=field_class.stored, numtype=int, field_boost=field_class.boost)
- # Zxy 如果字段类型是浮点数
- elif field_class.field_type == 'float':
- # Zxy 使用 NUMERIC 类型,并指定数字类型为浮点数
- schema_fields[field_class.index_fieldname] = NUMERIC(
- stored=field_class.stored, numtype=float, field_boost=field_class.boost)
- # Zxy 如果字段类型是布尔值
- elif field_class.field_type == 'boolean':
- # Zxy 使用 BOOLEAN 类型
- # Field boost isn't supported on BOOLEAN as of 1.8.2.
- schema_fields[field_class.index_fieldname] = BOOLEAN(
- stored=field_class.stored)
- # Zxy 如果字段类型是 N-gram
- elif field_class.field_type == 'ngram':
- # Zxy 使用 NGRAM 类型
- schema_fields[field_class.index_fieldname] = NGRAM(
- minsize=3, maxsize=15, stored=field_class.stored, field_boost=field_class.boost)
- # Zxy 如果字段类型是边 N-gram
- elif field_class.field_type == 'edge_ngram':
- # Zxy 使用 NGRAMWORDS 类型,并设置为从词首开始
- schema_fields[field_class.index_fieldname] = NGRAMWORDS(minsize=2, maxsize=15, at='start',
- stored=field_class.stored,
- field_boost=field_class.boost)
- else:
- # Zxy 默认情况下,使用 TEXT 类型,并配置中文分词器
- # schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost, sortable=True)
- schema_fields[field_class.index_fieldname] = TEXT(
- stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True)
- # Zxy 如果字段被标记为文档主内容
- if field_class.document is True:
- # Zxy 记录主内容字段名
- content_field_name = field_class.index_fieldname
- # Zxy 启用拼写建议功能
- schema_fields[field_class.index_fieldname].spelling = True
-
- # Zxy 如果除了内置字段外没有找到任何其他字段,则优雅地报错
- if len(schema_fields) <= initial_key_count:
- raise SearchBackendError(
- "No fields were found in any search_indexes. Please correct this before attempting to search.")
-
- # Zxy 返回主内容字段名和构建好的 Schema 对象
- return (content_field_name, Schema(**schema_fields))
-
- # Zxy 更新索引,将可迭代对象中的每个对象添加或更新到 Whoosh 索引中
- def update(self, index, iterable, commit=True):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
- # Zxy 创建一个异步写入器,用于高效地写入索引
- writer = AsyncWriter(self.index)
-
- # Zxy 遍历所有需要更新的对象
- for obj in iterable:
- try:
- # Zxy 准备要索引的文档数据
- doc = index.full_prepare(obj)
- except SkipDocument:
- # Zxy 如果对象被标记为跳过,则记录调试信息
- self.log.debug(u"Indexing for object `%s` skipped", obj)
- else:
- # Zxy 确保所有字段值都是 Whoosh 可以处理的格式
- for key in doc:
- doc[key] = self._from_python(doc[key])
-
- # Zxy 文档级别的权重提升在 Whoosh 2.5.0+ 版本中不再支持
- if 'boost' in doc:
- del doc['boost']
-
- try:
- # Zxy 使用写入器更新文档(如果存在则更新,否则创建)
- writer.update_document(**doc)
- except Exception as e:
- # Zxy 如果未配置为静默失败,则重新抛出异常
- if not self.silently_fail:
- raise
-
- # Zxy 记录错误信息,包含对象标识符,但不包含对象本身以避免编码问题
- self.log.error(
- u"%s while preparing object for update" %
- e.__class__.__name__,
- exc_info=True,
- extra={
- "data": {
- "index": index,
- "object": get_identifier(obj)}})
-
- # Zxy 如果可迭代对象不为空,则提交更改
- if len(iterable) > 0:
- # For now, commit no matter what, as we run into locking issues
- # otherwise.
- writer.commit()
-
- # Zxy 从索引中移除一个对象
- def remove(self, obj_or_string, commit=True):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
- # Zxy 获取对象的唯一标识符
- whoosh_id = get_identifier(obj_or_string)
-
- try:
- # Zxy 构建一个查询,根据 ID 查找文档并删除
- self.index.delete_by_query(
- q=self.parser.parse(
- u'%s:"%s"' %
- (ID, whoosh_id)))
- except Exception as e:
- # Zxy 如果未配置为静默失败,则重新抛出异常
- if not self.silently_fail:
- raise
-
- # Zxy 记录删除失败的错误
- self.log.error(
- "Failed to remove document '%s' from Whoosh: %s",
- whoosh_id,
- e,
- exc_info=True)
-
- # Zxy 清空索引,可以清空所有内容或指定模型的内容
- def clear(self, models=None, commit=True):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
-
- # Zxy 如果提供了模型列表,则检查其类型
- if models is not None:
- assert isinstance(models, (list, tuple))
-
- try:
- if models is None:
- # Zxy 如果没有指定模型,则删除整个索引
- self.delete_index()
- else:
- # Zxy 准备要删除的模型列表
- models_to_delete = []
-
- for model in models:
- # Zxy 为每个模型构建查询字符串
- models_to_delete.append(
- u"%s:%s" %
- (DJANGO_CT, get_model_ct(model)))
-
- # Zxy 使用 OR 连接多个模型查询,并删除匹配的文档
- self.index.delete_by_query(
- q=self.parser.parse(
- u" OR ".join(models_to_delete)))
- except Exception as e:
- # Zxy 如果未配置为静默失败,则重新抛出异常
- if not self.silently_fail:
- raise
-
- # Zxy 根据是否指定模型,记录不同的错误信息
- if models is not None:
- 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)
-
- # Zxy 物理删除整个索引
- def delete_index(self):
- # Per the Whoosh mailing list, if wiping out everything from the index,
- # it's much more efficient to simply delete the index files.
- # Zxy 如果使用文件存储,则直接删除索引目录
- if self.use_file_storage and os.path.exists(self.path):
- shutil.rmtree(self.path)
- # Zxy 如果使用内存存储,则清空存储
- elif not self.use_file_storage:
- self.storage.clean()
-
- # Zxy 重新初始化后端,创建新的空索引
- self.setup()
-
- # Zxy 优化索引,合并索引段以提高搜索性能
- def optimize(self):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
- # Zxy 执行 Whoosh 的优化操作
- self.index.optimize()
-
- # Zxy 根据偏移量计算 Whoosh 分页所需的页码和每页大小
- def calculate_page(self, start_offset=0, end_offset=None):
- # Zxy 防止 Whoosh 因 end_offset 小于等于 0 而抛出错误
- if end_offset is not None and end_offset <= 0:
- end_offset = 1
-
- # Zxy 初始化页码
- page_num = 0
-
- # Zxy 如果未指定结束偏移量,则设置一个很大的默认值
- if end_offset is None:
- end_offset = 1000000
-
- # Zxy 如果未指定起始偏移量,则默认为 0
- if start_offset is None:
- start_offset = 0
-
- # Zxy 计算每页的长度
- page_length = end_offset - start_offset
-
- # Zxy 如果页长度有效,则计算页码
- if page_length and page_length > 0:
- page_num = int(start_offset / page_length)
-
- # Zxy Whoosh 使用 1-based 页码,所以需要加 1
- page_num += 1
- return page_num, page_length
-
- # Zxy 执行搜索查询的核心方法
- @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,
- within=None,
- dwithin=None,
- distance_point=None,
- models=None,
- limit_to_registered_models=None,
- result_class=None,
- **kwargs):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 空查询字符串应返回无结果
- if len(query_string) == 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- # Zxy 确保查询字符串为正确的字符串类型
- query_string = force_str(query_string)
-
- # Zxy 单个字符(非通配符)查询会被停用词过滤器拦截,应返回无结果
- if len(query_string) <= 1 and query_string != u'*':
- return {
- 'results': [],
- 'hits': 0,
- }
-
- # Zxy 初始化排序方向为非逆序
- reverse = False
-
- # Zxy 如果提供了排序字段
- if sort_by is not None:
- # Zxy 确定是否需要反转结果,以及 Whoosh 是否能处理排序
- sort_by_list = []
- reverse_counter = 0
-
- # Zxy 统计逆序排序字段的数量
- for order_by in sort_by:
- if order_by.startswith('-'):
- reverse_counter += 1
-
- # Zxy Whoosh 要求所有排序字段的排序方向必须一致
- if reverse_counter and reverse_counter != len(sort_by):
- raise SearchBackendError("Whoosh requires all order_by fields"
- " to use the same sort direction")
-
- # Zxy 处理排序字段列表,去除 '-' 前缀并确定最终排序方向
- for order_by in sort_by:
- if order_by.startswith('-'):
- sort_by_list.append(order_by[1:])
- if len(sort_by_list) == 1:
- reverse = True
- else:
- sort_by_list.append(order_by)
- if len(sort_by_list) == 1:
- reverse = False
-
- # Zxy Whoosh 的 search_page 方法只接受单个排序字段
- sort_by = sort_by_list[0]
-
- # Zxy Whoosh 后端不支持分面搜索,发出警告
- if facets is not None:
- warnings.warn(
- "Whoosh does not handle faceting.",
- Warning,
- stacklevel=2)
-
- # Zxy Whoosh 后端不支持日期分面,发出警告
- if date_facets is not None:
- warnings.warn(
- "Whoosh does not handle date faceting.",
- Warning,
- stacklevel=2)
-
- # Zxy Whoosh 后端不支持查询分面,发出警告
- if query_facets is not None:
- warnings.warn(
- "Whoosh does not handle query faceting.",
- Warning,
- stacklevel=2)
-
- # Zxy 初始化用于存储缩小范围后的结果
- narrowed_results = None
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
-
- # Zxy 确定是否限制搜索到已注册的模型
- if limit_to_registered_models is None:
- limit_to_registered_models = getattr(
- settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
-
- # Zxy 根据传入的模型或配置构建模型选择列表
- if models and len(models):
- model_choices = sorted(get_model_ct(model) for model in models)
- elif limit_to_registered_models:
- # Zxy 使用缩小查询的方式,将结果限制在当前路由器处理的模型中
- model_choices = self.build_models_list()
- else:
- model_choices = []
-
- # Zxy 如果存在模型选择,则将其添加到缩小查询中
- if len(model_choices) > 0:
- if narrow_queries is None:
- narrow_queries = set()
-
- # Zxy 构建一个 OR 查询来限制模型类型
- narrow_queries.add(' OR '.join(
- ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
-
- # Zxy 初始化缩小查询的搜索器
- narrow_searcher = None
-
- # Zxy 如果存在缩小查询,则执行它们以获取一个结果集过滤器
- if narrow_queries is not None:
- # Zxy 这个操作可能很昂贵,但 Whoosh 中没有其他方法
- narrow_searcher = self.index.searcher()
-
- for nq in narrow_queries:
- recent_narrowed_results = narrow_searcher.search(
- self.parser.parse(force_str(nq)), limit=None)
-
- # Zxy 如果任何一个缩小查询返回空结果,则直接返回空
- if len(recent_narrowed_results) <= 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- # Zxy 将多个缩小查询的结果集进行交集过滤
- if narrowed_results:
- narrowed_results.filter(recent_narrowed_results)
- else:
- narrowed_results = recent_narrowed_results
-
- # Zxy 再次刷新索引以确保所有写入都可见
- self.index = self.index.refresh()
-
- # Zxy 如果索引中有文档,则执行搜索
- if self.index.doc_count():
- searcher = self.index.searcher()
- # Zxy 解析查询字符串
- parsed_query = self.parser.parse(query_string)
-
- # Zxy 如果查询无效或被停用词过滤,则优雅地恢复
- if parsed_query is None:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- # Zxy 计算分页参数
- page_num, page_length = self.calculate_page(
- start_offset, end_offset)
-
- # Zxy 准备搜索参数
- search_kwargs = {
- 'pagelen': page_length,
- 'sortedby': sort_by,
- 'reverse': reverse,
- }
-
- # Zxy 如果存在缩小范围的结果,则将其作为过滤器
- if narrowed_results is not None:
- search_kwargs['filter'] = narrowed_results
-
- try:
- # Zxy 执行分页搜索
- raw_page = searcher.search_page(
- parsed_query,
- page_num,
- **search_kwargs
- )
- except ValueError:
- # Zxy 如果页码无效,则返回空结果
- if not self.silently_fail:
- raise
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Zxy 兼容 Whoosh 2.5.1 的 bug:请求过高的页码会返回错误的页
- if raw_page.pagenum < page_num:
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Zxy 处理原始搜索结果,转换为 Haystack 的 SearchResult 对象
- results = self._process_results(
- raw_page,
- highlight=highlight,
- query_string=query_string,
- spelling_query=spelling_query,
- result_class=result_class)
- # Zxy 关闭主搜索器
- searcher.close()
-
- # Zxy 关闭缩小查询的搜索器
- if hasattr(narrow_searcher, 'close'):
- narrow_searcher.close()
-
- return results
- else:
- # Zxy 如果索引为空,但仍需处理拼写建议
- spelling_suggestion = None
- if self.include_spelling:
- if spelling_query:
- spelling_suggestion = self.create_spelling_suggestion(
- spelling_query)
- else:
- spelling_suggestion = self.create_spelling_suggestion(
- query_string)
-
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': spelling_suggestion,
- }
-
- # Zxy 实现“更多类似于此”功能,根据给定模型实例查找相似文档
- 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):
- # Zxy 如果后端尚未初始化,则先进行设置
- if not self.setup_complete:
- self.setup()
-
- # Zxy 获取模型的真实类,避免使用延迟加载的模型类
- # Deferred models will have a different class ("RealClass_Deferred_fieldname")
- # which won't be in our registry:
- model_klass = model_instance._meta.concrete_model
-
- # Zxy 获取主内容字段名,用于相似性分析
- field_name = self.content_field_name
- # Zxy 初始化缩小查询集合
- narrow_queries = set()
- # Zxy 初始化缩小范围后的结果集
- narrowed_results = None
- # Zxy 刷新索引以获取最新状态
- self.index = self.index.refresh()
-
- # Zxy 确定是否限制搜索到已注册的模型
- if limit_to_registered_models is None:
- limit_to_registered_models = getattr(
- settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
-
- # Zxy 根据传入的模型或配置构建模型选择列表
- if models and len(models):
- model_choices = sorted(get_model_ct(model) for model in models)
- elif limit_to_registered_models:
- # Zxy 使用缩小查询的方式,将结果限制在当前路由器处理的模型中
- model_choices = self.build_models_list()
- else:
- model_choices = []
-
- # Zxy 如果存在模型选择,则将其添加到缩小查询中
- if len(model_choices) > 0:
- if narrow_queries is None:
- narrow_queries = set()
-
- # Zxy 构建一个 OR 查询来限制模型类型
- narrow_queries.add(' OR '.join(
- ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
-
- # Zxy 如果提供了额外的查询字符串,则也添加到缩小查询中
- if additional_query_string and additional_query_string != '*':
- narrow_queries.add(additional_query_string)
-
- # Zxy 初始化缩小查询的搜索器
- narrow_searcher = None
-
- # Zxy 如果存在缩小查询,则执行它们以获取一个结果集过滤器
- if narrow_queries is not None:
- # Zxy 这个操作可能很昂贵,但 Whoosh 中没有其他方法
- narrow_searcher = self.index.searcher()
-
- for nq in narrow_queries:
- recent_narrowed_results = narrow_searcher.search(
- self.parser.parse(force_str(nq)), limit=None)
-
- # Zxy 如果任何一个缩小查询返回空结果,则直接返回空
- if len(recent_narrowed_results) <= 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- # Zxy 将多个缩小查询的结果集进行交集过滤
- if narrowed_results:
- narrowed_results.filter(recent_narrowed_results)
- else:
- narrowed_results = recent_narrowed_results
-
- # Zxy 计算分页参数
- page_num, page_length = self.calculate_page(start_offset, end_offset)
-
- # Zxy 再次刷新索引以确保所有写入都可见
- self.index = self.index.refresh()
- # Zxy 初始化原始结果为空
- raw_results = EmptyResults()
-
- # Zxy 如果索引中有文档,则执行“更多类似于此”查询
- if self.index.doc_count():
- # Zxy 构建一个查询以找到当前模型实例对应的索引文档
- query = "%s:%s" % (ID, get_identifier(model_instance))
- searcher = self.index.searcher()
- parsed_query = self.parser.parse(query)
- # Zxy 搜索当前文档
- results = searcher.search(parsed_query)
-
- # Zxy 如果找到了当前文档,则调用其 more_like_this 方法
- if len(results):
- # Zxy 获取与当前文档相似的其他文档
- raw_results = results[0].more_like_this(
- field_name, top=end_offset)
-
- # Zxy 如果存在缩小范围的结果,则将其作为过滤器应用于相似结果
- if narrowed_results is not None and hasattr(raw_results, 'filter'):
- raw_results.filter(narrowed_results)
-
- try:
- # Zxy 将原始结果集包装成分页对象
- raw_page = ResultsPage(raw_results, page_num, page_length)
- except ValueError:
- # Zxy 如果页码无效,则返回空结果
- if not self.silently_fail:
- raise
-
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Zxy 兼容 Whoosh 2.5.1 的 bug:请求过高的页码会返回错误的页
- if raw_page.pagenum < page_num:
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Zxy 处理原始搜索结果,转换为 Haystack 的 SearchResult 对象
- results = self._process_results(raw_page, result_class=result_class)
- # Zxy 关闭主搜索器
- searcher.close()
-
- # Zxy 关闭缩小查询的搜索器
- if hasattr(narrow_searcher, 'close'):
- narrow_searcher.close()
-
- return results
-
-
- # Zxy 处理 Whoosh 返回的原始搜索结果,转换为 Haystack 标准格式
- def _process_results(
- self,
- raw_page,
- highlight=False,
- query_string='',
- spelling_query=None,
- result_class=None):
- # Zxy 导入 haystack 连接管理器
- from haystack import connections
- # Zxy 初始化结果列表
- results = []
-
- # Zxy 在切片之前获取总命中数,这对于分页至关重要
- hits = len(raw_page)
-
- # Zxy 如果未指定结果类,则使用默认的 SearchResult
- if result_class is None:
- result_class = SearchResult
-
- # Zxy 初始化分面和拼写建议
- facets = {}
- spelling_suggestion = None
- # Zxy 获取统一索引对象和已注册的模型列表
- unified_index = connections[self.connection_alias].get_unified_index()
- indexed_models = unified_index.get_indexed_models()
-
- # Zxy 遍历原始结果页中的每个文档
- for doc_offset, raw_result in enumerate(raw_page):
- # Zxy 获取文档的得分
- score = raw_page.score(doc_offset) or 0
- # Zxy 从文档中解析出应用标签和模型名
- app_label, model_name = raw_result[DJANGO_CT].split('.')
- # Zxy 初始化额外字段字典
- additional_fields = {}
- # Zxy 根据应用标签和模型名获取模型类
- model = haystack_get_model(app_label, model_name)
-
- # Zxy 确保模型存在且已注册到索引
- if model and model in indexed_models:
- # Zxy 遍历文档中的所有字段
- for key, value in raw_result.items():
- # Zxy 获取该模型对应的索引
- index = unified_index.get_index(model)
- string_key = str(key)
-
- # Zxy 如果字段在索引定义中,并且有转换方法
- if string_key in index.fields and hasattr(
- index.fields[string_key], 'convert'):
- # Zxy 特殊处理多值字段
- if index.fields[string_key].is_multivalued:
- if value is None or len(value) == 0:
- additional_fields[string_key] = []
- else:
- additional_fields[string_key] = value.split(
- ',')
- else:
- # Zxy 使用索引字段定义的转换方法
- additional_fields[string_key] = index.fields[string_key].convert(
- value)
- else:
- # Zxy 否则使用通用的 Python 类型转换
- additional_fields[string_key] = self._to_python(value)
-
- # Zxy 删除 Haystack 内部字段,不返回给用户
- del (additional_fields[DJANGO_CT])
- del (additional_fields[DJANGO_ID])
-
- # Zxy 如果需要高亮显示
- if highlight:
- # Zxy 创建词干分析器和 HTML 格式化器
- sa = StemmingAnalyzer()
- formatter = WhooshHtmlFormatter('em')
- # Zxy 从查询字符串中提取词条
- terms = [token.text for token in sa(query_string)]
-
- # Zxy 调用 Whoosh 的高亮方法
- whoosh_result = whoosh_highlight(
- additional_fields.get(self.content_field_name),
- terms,
- sa,
- ContextFragmenter(),
- formatter
- )
- # Zxy 将高亮结果添加到额外字段中
- additional_fields['highlighted'] = {
- self.content_field_name: [whoosh_result],
- }
-
- # Zxy 创建 SearchResult 对象并添加到结果列表
- result = result_class(
- app_label,
- model_name,
- raw_result[DJANGO_ID],
- score,
- **additional_fields)
- results.append(result)
- else:
- # Zxy 如果模型未注册,则减少总命中数
- hits -= 1
-
- # Zxy 如果启用了拼写建议
- if self.include_spelling:
- if spelling_query:
- spelling_suggestion = self.create_spelling_suggestion(
- spelling_query)
- else:
- spelling_suggestion = self.create_spelling_suggestion(
- query_string)
-
- # Zxy 返回包含结果、命中数、分面和拼写建议的字典
- return {
- 'results': results,
- 'hits': hits,
- 'facets': facets,
- 'spelling_suggestion': spelling_suggestion,
- }
-
- # Zxy 根据查询字符串创建拼写建议
- def create_spelling_suggestion(self, query_string):
- # Zxy 初始化拼写建议
- spelling_suggestion = None
- # Zxy 获取索引的读取器和校正器
- reader = self.index.reader()
- corrector = reader.corrector(self.content_field_name)
- # Zxy 清理查询字符串
- cleaned_query = force_str(query_string)
-
- # Zxy 如果查询字符串为空,直接返回
- if not query_string:
- return spelling_suggestion
-
- # Zxy 移除查询中的保留字
- for rev_word in self.RESERVED_WORDS:
- cleaned_query = cleaned_query.replace(rev_word, '')
-
- # Zxy 移除查询中的保留字符
- for rev_char in self.RESERVED_CHARACTERS:
- cleaned_query = cleaned_query.replace(rev_char, '')
-
- # Zxy 将清理后的查询拆分为单词列表
- query_words = cleaned_query.split()
- suggested_words = []
-
- # Zxy 为每个单词查找拼写建议
- for word in query_words:
- suggestions = corrector.suggest(word, limit=1)
-
- if len(suggestions) > 0:
- suggested_words.append(suggestions[0])
-
- # Zxy 将建议的单词重新组合成字符串
- spelling_suggestion = ' '.join(suggested_words)
- return spelling_suggestion
-
- # Zxy 将 Python 值转换为 Whoosh 可用的字符串格式
- def _from_python(self, value):
- """
- Converts Python values to a string for Whoosh.
-
- Code courtesy of pysolr.
- """
- # Zxy 处理日期时间对象
- if hasattr(value, 'strftime'):
- # Zxy 如果只有日期没有时间,则将时间部分设为 0
- if not hasattr(value, 'hour'):
- value = datetime(value.year, value.month, value.day, 0, 0, 0)
- # Zxy 处理布尔值
- elif isinstance(value, bool):
- if value:
- value = 'true'
- else:
- value = 'false'
- # Zxy 处理列表或元组,用逗号连接
- elif isinstance(value, (list, tuple)):
- value = u','.join([force_str(v) for v in value])
- # Zxy 处理数字,保持原样
- elif isinstance(value, (six.integer_types, float)):
- # Leave it alone.
- pass
- else:
- # Zxy 其他类型强制转换为字符串
- value = force_str(value)
- return value
-
- # Zxy 将 Whoosh 的值转换为原生 Python 值
- def _to_python(self, value):
- """
- Converts values from Whoosh to native Python values.
-
- A port of the same method in pysolr, as they deal with data the same way.
- """
- # Zxy 处理布尔字符串
- if value == 'true':
- return True
- elif value == 'false':
- return False
-
- # Zxy 尝试解析日期时间字符串
- if value and isinstance(value, six.string_types):
- possible_datetime = DATETIME_REGEX.search(value)
-
- if possible_datetime:
- date_values = possible_datetime.groupdict()
-
- for dk, dv in date_values.items():
- date_values[dk] = int(dv)
-
- return datetime(
- date_values['year'],
- date_values['month'],
- date_values['day'],
- date_values['hour'],
- date_values['minute'],
- date_values['second'])
-
- # Zxy 尝试使用 json 解复杂数据类型
- try:
- # Attempt to use json to load the values.
- converted_value = json.loads(value)
-
- # Try to handle most built-in types.
- if isinstance(
- converted_value,
- (list,
- tuple,
- set,
- dict,
- six.integer_types,
- float,
- complex)):
- return converted_value
- except BaseException:
- # If it fails (SyntaxError or its ilk) or we don't trust it,
- # continue on.
- pass
-
- # Zxy 如果都无法转换,则返回原始值
- return value
-
- # Zxy 定义 Whoosh 搜索查询类,继承自 Haystack 的 BaseSearchQuery
-class WhooshSearchQuery(BaseSearchQuery):
- # Zxy 将日期时间对象转换为 Whoosh 查询所需的字符串格式
- def _convert_datetime(self, date):
- # Zxy 如果包含时间,则转换为完整格式
- if hasattr(date, 'hour'):
- return force_str(date.strftime('%Y%m%d%H%M%S'))
- else:
- # Zxy 如果只有日期,则补充零时间
- return force_str(date.strftime('%Y%m%d000000'))
-
- # Zxy 清理查询片段,转义 Whoosh 的保留字符
- def clean(self, query_fragment):
- """
- Provides a mechanism for sanitizing user input before presenting the
- value to the backend.
-
- Whoosh 1.X differs here in that you can no longer use a backslash
- to escape reserved characters. Instead, the whole word should be
- quoted.
- """
- # Zxy 将查询片段按空格分割成单词
- words = query_fragment.split()
- cleaned_words = []
-
- # Zxy 遍历每个单词进行清理
- for word in words:
- # Zxy 如果是保留字,则转为小写
- if word in self.backend.RESERVED_WORDS:
- word = word.replace(word, word.lower())
-
- # Zxy 如果包含保留字符,则用单引号将整个单词括起来
- for char in self.backend.RESERVED_CHARACTERS:
- if char in word:
- word = "'%s'" % word
- break
-
- cleaned_words.append(word)
-
- # Zxy 将清理后的单词重新组合
- return ' '.join(cleaned_words)
-
- # Zxy 构建查询片段,根据字段、过滤类型和值生成 Whoosh 查询语法
- def build_query_fragment(self, field, filter_type, value):
- # Zxy 导入 haystack 连接管理器
- from haystack import connections
- query_frag = ''
- is_datetime = False
-
- # Zxy 如果值没有 input_type_name 属性,则进行类型推断
- if not hasattr(value, 'input_type_name'):
- # Handle when we've got a ``ValuesListQuerySet``...
- if hasattr(value, 'values_list'):
- value = list(value)
-
- if hasattr(value, 'strftime'):
- is_datetime = True
-
- if isinstance(value, six.string_types) and value != ' ':
- # It's not an ``InputType``. Assume ``Clean``.
- value = Clean(value)
- else:
- value = PythonData(value)
-
- # Zxy 使用 InputType 准备查询值
- prepared_value = value.prepare(self)
-
- # Zxy 如果准备好的值不是集合类型,则转换为 Whoosh 可用的格式
- if not isinstance(prepared_value, (set, list, tuple)):
- # Then convert whatever we get back to what pysolr wants if needed.
- prepared_value = self.backend._from_python(prepared_value)
-
- # 'content' is a special reserved word, much like 'pk' in
- # Django's ORM layer. It indicates 'no special field'.
- # Zxy 'content' 是特殊字段,代表所有可搜索内容
- if field == 'content':
- index_fieldname = ''
- else:
- # Zxy 获取字段在索引中的真实名称
- index_fieldname = u'%s:' % connections[self._using].get_unified_index(
- ).get_index_fieldname(field)
-
- # Zxy 定义不同过滤类型对应的 Whoosh 查询模板
- filter_types = {
- 'content': '%s',
- 'contains': '*%s*',
- 'endswith': "*%s",
- 'startswith': "%s*",
- 'exact': '%s',
- 'gt': "{%s to}",
- 'gte': "[%s to]",
- 'lt': "{to %s}",
- 'lte': "[to %s]",
- 'fuzzy': u'%s~',
- }
- # Zxy 如果值不需要后处理,则直接使用
- if value.post_process is False:
- query_frag = prepared_value
- else:
- # Zxy 根据不同的过滤类型构建查询片段
- if filter_type in [
- 'content',
- 'contains',
- 'startswith',
- 'endswith',
- 'fuzzy']:
- # Zxy 如果输入类型是精确匹配,则直接使用值
- if value.input_type_name == 'exact':
- query_frag = prepared_value
- else:
- # Iterate over terms & incorportate the converted form of
- # each into the query.
- terms = []
-
- if isinstance(prepared_value, six.string_types):
- possible_values = prepared_value.split(' ')
- else:
- if is_datetime is True:
- prepared_value = self._convert_datetime(
- prepared_value)
-
- possible_values = [prepared_value]
-
- for possible_value in possible_values:
- terms.append(
- filter_types[filter_type] %
- self.backend._from_python(possible_value))
-
- if len(terms) == 1:
- query_frag = terms[0]
- else:
- query_frag = u"(%s)" % " AND ".join(terms)
- # Zxy 处理 'in' 过滤类型
- elif filter_type == 'in':
- in_options = []
-
- for possible_value in prepared_value:
- is_datetime = False
-
- if hasattr(possible_value, 'strftime'):
- is_datetime = True
-
- pv = self.backend._from_python(possible_value)
-
- if is_datetime is True:
- pv = self._convert_datetime(pv)
-
- if isinstance(pv, six.string_types) and not is_datetime:
- in_options.append('"%s"' % pv)
- else:
- in_options.append('%s' % pv)
-
- query_frag = "(%s)" % " OR ".join(in_options)
- # Zxy 处理 'range' 过滤类型
- elif filter_type == 'range':
- start = self.backend._from_python(prepared_value[0])
- end = self.backend._from_python(prepared_value[1])
-
- if hasattr(prepared_value[0], 'strftime'):
- start = self._convert_datetime(start)
-
- if hasattr(prepared_value[1], 'strftime'):
- end = self._convert_datetime(end)
-
- query_frag = u"[%s to %s]" % (start, end)
- # Zxy 处理 'exact' 过滤类型
- elif filter_type == 'exact':
- if value.input_type_name == 'exact':
- query_frag = prepared_value
- else:
- prepared_value = Exact(prepared_value).prepare(self)
- query_frag = filter_types[filter_type] % prepared_value
- else:
- # Zxy 处理其他类型(如 gt, gte, lt, lte)
- if is_datetime is True:
- prepared_value = self._convert_datetime(prepared_value)
-
- query_frag = filter_types[filter_type] % prepared_value
-
- # Zxy 如果查询片段不为空且不是原始查询,则用括号括起来
- if len(query_frag) and not isinstance(value, Raw):
- if not query_frag.startswith('(') and not query_frag.endswith(')'):
- query_frag = "(%s)" % query_frag
- return u"%s%s" % (index_fieldname, query_frag)
-
- # if not filter_type in ('in', 'range'):
- # # 'in' is a bit of a special case, as we don't want to
- # # convert a valid list/tuple to string. Defer handling it
- # # until later...
- # value = self.backend._from_python(value)
-
-# Zxy 定义 Whoosh 引擎类,继承自 Haystack 的 BaseEngine
-class WhooshEngine(BaseEngine):
- # Zxy 指定后端和查询类
- backend = WhooshSearchBackend
- query = WhooshSearchQuery
diff --git a/src/DjangoBlog-master/djangoblog/wsgi.py b/src/DjangoBlog-master/djangoblog/wsgi.py
deleted file mode 100644
index 730430e..0000000
--- a/src/DjangoBlog-master/djangoblog/wsgi.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-WSGI config for djangoblog project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
-"""
-
-# Zxy导入os模块,用于操作环境变量
-import os
-
-# Zxy从Django中导入get_wsgi_application函数,用于获取WSGI应用
-from django.core.wsgi import get_wsgi_application
-
-# Zxy设置环境变量DJANGO_SETTINGS_MODULE,指定Django项目的配置文件
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
-
-# Zxy获取WSGI应用实例,用于部署
-application = get_wsgi_application()
\ No newline at end of file