From 79735ba0abf6fd744ab858bbc8ca16d7d844201e Mon Sep 17 00:00:00 2001
From: psr <2475287345@qq.com>
Date: Sun, 2 Nov 2025 15:12:08 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4psr=E4=BB=A3=E7=A0=81?=
=?UTF-8?q?=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/DjangoBlog-master/djangoblog/__init__.py | 3 +
.../djangoblog/admin_site.py | 20 ++-
src/DjangoBlog-master/djangoblog/apps.py | 12 +-
.../djangoblog/blog_signals.py | 30 ++++
.../djangoblog/elasticsearch_backend.py | 74 ++++++++-
.../djangoblog/logentryadmin.py | 23 +++
src/DjangoBlog-master/djangoblog/settings.py | 54 ++++++-
src/DjangoBlog-master/djangoblog/sitemap.py | 26 +++
.../djangoblog/spider_notify.py | 10 ++
src/DjangoBlog-master/djangoblog/tests.py | 7 +
src/DjangoBlog-master/djangoblog/urls.py | 21 ++-
src/DjangoBlog-master/djangoblog/utils.py | 63 +++++++-
.../djangoblog/whoosh_cn_backend.py | 152 +++++++++++++++---
src/DjangoBlog-master/djangoblog/wsgi.py | 5 +
14 files changed, 457 insertions(+), 43 deletions(-)
diff --git a/src/DjangoBlog-master/djangoblog/__init__.py b/src/DjangoBlog-master/djangoblog/__init__.py
index 1e205f40..c9794562 100644
--- a/src/DjangoBlog-master/djangoblog/__init__.py
+++ b/src/DjangoBlog-master/djangoblog/__init__.py
@@ -1 +1,4 @@
+# psr:设置默认的应用配置类
+# psr:该配置指向 djangoblog 应用下的 apps.py 文件中的 DjangoblogAppConfig 类
+# psr:Django 会使用这个配置类来加载应用的自定义设置和行为
default_app_config = 'djangoblog.apps.DjangoblogAppConfig'
diff --git a/src/DjangoBlog-master/djangoblog/admin_site.py b/src/DjangoBlog-master/djangoblog/admin_site.py
index f1204059..56fcb110 100644
--- a/src/DjangoBlog-master/djangoblog/admin_site.py
+++ b/src/DjangoBlog-master/djangoblog/admin_site.py
@@ -1,8 +1,10 @@
+#psr:导入Django管理站点相关模块和模型
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
+#psr:导入项目中各个应用的admin和models模块
from accounts.admin import *
from blog.admin import *
from blog.models import *
@@ -16,14 +18,16 @@ from owntracks.models import *
from servermanager.admin import *
from servermanager.models import *
-
+#psr:自定义Django管理站点类,用于统一管理博客系统所有模型
class DjangoBlogAdminSite(AdminSite):
+ #psr:设置管理站点的头部标题
site_header = 'djangoblog administration'
+ #psr:设置管理站点的标题
site_title = 'djangoblog site admin'
-
+ #psr:初始化管理站点,设置名称为'admin'
def __init__(self, name='admin'):
super().__init__(name)
-
+ #psr:重写权限检查方法,只允许超级用户访问管理站点
def has_permission(self, request):
return request.user.is_superuser
@@ -37,9 +41,10 @@ class DjangoBlogAdminSite(AdminSite):
# ]
# return urls + my_urls
-
+#psr:创建DjangoBlog管理站点实例
admin_site = DjangoBlogAdminSite(name='admin')
+#psr:注册博客相关的模型到管理站点
admin_site.register(Article, ArticlelAdmin)
admin_site.register(Category, CategoryAdmin)
admin_site.register(Tag, TagAdmin)
@@ -47,18 +52,25 @@ admin_site.register(Links, LinksAdmin)
admin_site.register(SideBar, SideBarAdmin)
admin_site.register(BlogSettings, BlogSettingsAdmin)
+#psr:注册服务器管理相关的模型到管理站点
admin_site.register(commands, CommandsAdmin)
admin_site.register(EmailSendLog, EmailSendLogAdmin)
+#psr:注册用户管理相关的模型到管理站点
admin_site.register(BlogUser, BlogUserAdmin)
+#psr:注册评论相关的模型到管理站点
admin_site.register(Comment, CommentAdmin)
+#psr:注册OAuth认证相关的模型到管理站点
admin_site.register(OAuthUser, OAuthUserAdmin)
admin_site.register(OAuthConfig, OAuthConfigAdmin)
+#psr:注册OwnTracks位置追踪相关的模型到管理站点
admin_site.register(OwnTrackLog, OwnTrackLogsAdmin)
+#psr:注册Django自带的Site模型到管理站点
admin_site.register(Site, SiteAdmin)
+#psr:注册Django自带的LogEntry模型到管理站点
admin_site.register(LogEntry, LogEntryAdmin)
diff --git a/src/DjangoBlog-master/djangoblog/apps.py b/src/DjangoBlog-master/djangoblog/apps.py
index d29e318a..f6e72668 100644
--- a/src/DjangoBlog-master/djangoblog/apps.py
+++ b/src/DjangoBlog-master/djangoblog/apps.py
@@ -1,11 +1,19 @@
+#psr:导入Django应用配置基类
from django.apps import AppConfig
+#psr:定义DjangoBlog应用的配置类,继承自AppConfig
class DjangoblogAppConfig(AppConfig):
+ #psr:设置默认的自动字段类型为BigAutoField
default_auto_field = 'django.db.models.BigAutoField'
+ #psr:指定应用名称
name = 'djangoblog'
+ #psr:重写ready方法,在应用启动时执行初始化操作
def ready(self):
+ #psr:调用父类的ready方法
super().ready()
- # Import and load plugins here
- from .plugin_manage.loader import load_plugins
+ # Import and load plugins here
+ from .plugin_manage.loader import load_plugins #psr:导入并加载插件系统
+
+ #psr:执行插件加载函数
load_plugins()
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/blog_signals.py b/src/DjangoBlog-master/djangoblog/blog_signals.py
index 393f441c..d0433388 100644
--- a/src/DjangoBlog-master/djangoblog/blog_signals.py
+++ b/src/DjangoBlog-master/djangoblog/blog_signals.py
@@ -1,3 +1,4 @@
+#psr 导入线程、日志、Django信号、邮件、模型信号等相关模块
import _thread
import logging
@@ -9,6 +10,7 @@ from django.core.mail import EmailMultiAlternatives
from django.db.models.signals import post_save
from django.dispatch import receiver
+#psr 导入项目相关模块和工具函数
from comments.models import Comment
from comments.utils import send_comment_email
from djangoblog.spider_notify import SpiderNotify
@@ -16,19 +18,24 @@ from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, del
from djangoblog.utils import get_current_site
from oauth.models import OAuthUser
+#psr 获取日志记录器实例
logger = logging.getLogger(__name__)
+#psr 定义OAuth用户登录信号和发送邮件信号
oauth_user_login_signal = django.dispatch.Signal(['id'])
send_email_signal = django.dispatch.Signal(
['emailto', 'title', 'content'])
+#psr 发送邮件信号处理函数,用于发送HTML格式邮件
@receiver(send_email_signal)
def send_email_signal_handler(sender, **kwargs):
+ #psr 从信号参数中获取邮件信息
emailto = kwargs['emailto']
title = kwargs['title']
content = kwargs['content']
+ #psr 创建邮件消息对象并设置HTML内容类型
msg = EmailMultiAlternatives(
title,
content,
@@ -36,6 +43,7 @@ def send_email_signal_handler(sender, **kwargs):
to=emailto)
msg.content_subtype = "html"
+ #psr 创建邮件发送日志记录
from servermanager.models import EmailSendLog
log = EmailSendLog()
log.title = title
@@ -43,27 +51,35 @@ def send_email_signal_handler(sender, **kwargs):
log.emailto = ','.join(emailto)
try:
+ #psr 发送邮件并记录发送结果
result = msg.send()
log.send_result = result > 0
except Exception as e:
+ #psr 处理邮件发送异常
logger.error(f"失败邮箱号: {emailto}, {e}")
log.send_result = False
+ #psr 保存邮件发送日志
log.save()
+#psr OAuth用户登录信号处理函数,用于处理用户头像等登录后操作
@receiver(oauth_user_login_signal)
def oauth_user_login_signal_handler(sender, **kwargs):
+ #psr 获取OAuth用户信息
id = kwargs['id']
oauthuser = OAuthUser.objects.get(id=id)
site = get_current_site().domain
+ #psr 如果用户头像不在当前站点,则保存用户头像到本地
if oauthuser.picture and not oauthuser.picture.find(site) >= 0:
from djangoblog.utils import save_user_avatar
oauthuser.picture = save_user_avatar(oauthuser.picture)
oauthuser.save()
+ #psr 删除侧边栏缓存
delete_sidebar_cache()
+#psr 模型保存后回调函数,用于处理模型保存后的缓存清理和通知操作
@receiver(post_save)
def model_post_save_callback(
sender,
@@ -74,19 +90,24 @@ def model_post_save_callback(
update_fields,
**kwargs):
clearcache = False
+ #psr 如果是日志条目则直接返回
if isinstance(instance, LogEntry):
return
+ #psr 如果实例有获取完整URL的方法
if 'get_full_url' in dir(instance):
is_update_views = update_fields == {'views'}
+ #psr 如果不是测试环境且不是仅更新浏览量,则通知搜索引擎
if not settings.TESTING and not is_update_views:
try:
notify_url = instance.get_full_url()
SpiderNotify.baidu_notify([notify_url])
except Exception as ex:
logger.error("notify sipder", ex)
+ #psr 如果不是仅更新浏览量则需要清理缓存
if not is_update_views:
clearcache = True
+ #psr 如果是评论实例且已启用
if isinstance(instance, Comment):
if instance.is_enable:
path = instance.article.get_absolute_url()
@@ -94,29 +115,38 @@ def model_post_save_callback(
if site.find(':') > 0:
site = site[0:site.find(':')]
+ #psr 过期文章详情页面缓存
expire_view_cache(
path,
servername=site,
serverport=80,
key_prefix='blogdetail')
+ #psr 删除SEO处理器缓存
if cache.get('seo_processor'):
cache.delete('seo_processor')
+ #psr 删除文章评论缓存
comment_cache_key = 'article_comments_{id}'.format(
id=instance.article.id)
cache.delete(comment_cache_key)
+ #psr 删除侧边栏和视图缓存
delete_sidebar_cache()
delete_view_cache('article_comments', [str(instance.article.pk)])
+ #psr 异步发送评论邮件通知
_thread.start_new_thread(send_comment_email, (instance,))
+ #psr 如果需要清理缓存则清空所有缓存
if clearcache:
cache.clear()
+#psr 用户登录和登出回调函数,用于处理用户认证相关缓存清理
@receiver(user_logged_in)
@receiver(user_logged_out)
def user_auth_callback(sender, request, user, **kwargs):
+ #psr 如果用户存在且有用户名
if user and user.username:
logger.info(user)
+ #psr 删除侧边栏缓存
delete_sidebar_cache()
# cache.clear()
diff --git a/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py b/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py
index 4afe4981..6c0c1fd6 100644
--- a/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py
+++ b/src/DjangoBlog-master/djangoblog/elasticsearch_backend.py
@@ -1,3 +1,4 @@
+#psr:导入所需的编码处理、Elasticsearch查询、Haystack搜索框架等相关模块
from django.utils.encoding import force_str
from elasticsearch_dsl import Q
from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query
@@ -5,103 +6,144 @@ from haystack.forms import ModelSearchForm
from haystack.models import SearchResult
from haystack.utils import log as logging
+#psr:导入博客应用的Elasticsearch文档和模型
from blog.documents import ArticleDocument, ArticleDocumentManager
from blog.models import Article
+#psr:获取日志记录器实例
logger = logging.getLogger(__name__)
+#psr:定义Elasticsearch搜索后端类,继承自BaseSearchBackend
class ElasticSearchBackend(BaseSearchBackend):
+ #psr:初始化Elasticsearch后端,设置连接别名和选项
def __init__(self, connection_alias, **connection_options):
super(
ElasticSearchBackend,
self).__init__(
connection_alias,
**connection_options)
+ #psr:创建文章文档管理器实例
self.manager = ArticleDocumentManager()
+ #psr:启用拼写检查功能
self.include_spelling = True
+ #psr:获取模型数据并转换为文档格式
def _get_models(self, iterable):
+ #psr:如果传入迭代器为空则获取所有文章对象
models = iterable if iterable and iterable[0] else Article.objects.all()
+ #psr:将模型转换为Elasticsearch文档
docs = self.manager.convert_to_doc(models)
return docs
+ #psr:创建索引,包括创建索引结构和重建文档数据
def _create(self, models):
+ #psr:创建Elasticsearch索引
self.manager.create_index()
+ #psr:获取模型文档
docs = self._get_models(models)
+ #psr:重建索引数据
self.manager.rebuild(docs)
+ #psr:删除指定模型数据
def _delete(self, models):
+ #psr:遍历模型列表并删除每个模型
for m in models:
m.delete()
return True
+ #psr:重建索引数据
def _rebuild(self, models):
+ #psr:如果模型为空则获取所有文章对象
models = models if models else Article.objects.all()
+ #psr:转换模型为文档格式
docs = self.manager.convert_to_doc(models)
+ #psr:更新文档数据
self.manager.update_docs(docs)
+ #psr:更新索引中的文档数据
def update(self, index, iterable, commit=True):
-
+ #psr:获取模型文档
models = self._get_models(iterable)
+ #psr:更新文档到Elasticsearch
self.manager.update_docs(models)
+ #psr:从索引中移除指定对象
def remove(self, obj_or_string):
+ #psr:获取模型文档并删除
models = self._get_models([obj_or_string])
self._delete(models)
+ #psr:清空索引数据
def clear(self, models=None, commit=True):
+ #psr:调用remove方法清空数据
self.remove(None)
+ #psr:静态方法,获取搜索建议词
@staticmethod
def get_suggestion(query: str) -> str:
"""获取推荐词, 如果没有找到添加原搜索词"""
+ #psr:构建Elasticsearch搜索查询,使用term建议器
search = ArticleDocument.search() \
.query("match", body=query) \
.suggest('suggest_search', query, term={'field': 'body'}) \
.execute()
+ #psr:处理建议结果,提取建议词
keywords = []
for suggest in search.suggest.suggest_search:
if suggest["options"]:
+ #psr:如果有建议选项则使用建议词
keywords.append(suggest["options"][0]["text"])
else:
+ #psr:如果没有建议选项则使用原词
keywords.append(suggest["text"])
+ #psr:将关键词组合成字符串返回
return ' '.join(keywords)
+ #psr:执行搜索查询的核心方法
@log_query
def search(self, query_string, **kwargs):
+ #psr:记录搜索查询日志
logger.info('search query_string:' + query_string)
+ #psr:获取分页偏移量参数
start_offset = kwargs.get('start_offset')
end_offset = kwargs.get('end_offset')
- # 推荐词搜索
+ #psr:根据是否启用建议功能获取搜索建议词
if getattr(self, "is_suggest", None):
suggestion = self.get_suggestion(query_string)
else:
suggestion = query_string
+ #psr:构建Elasticsearch布尔查询条件
q = Q('bool',
should=[Q('match', body=suggestion), Q('match', title=suggestion)],
minimum_should_match="70%")
+ #psr:构建完整的Elasticsearch搜索查询
search = ArticleDocument.search() \
.query('bool', filter=[q]) \
.filter('term', status='p') \
.filter('term', type='a') \
.source(False)[start_offset: end_offset]
+ #psr:执行搜索并获取结果
results = search.execute()
+ #psr:获取搜索结果总数
hits = results['hits'].total
+ #psr:处理原始搜索结果
raw_results = []
for raw_result in results['hits']['hits']:
+ #psr:设置应用标签和模型名称
app_label = 'blog'
model_name = 'Article'
additional_fields = {}
+ #psr:创建搜索结果对象
result_class = SearchResult
result = result_class(
@@ -111,9 +153,11 @@ class ElasticSearchBackend(BaseSearchBackend):
raw_result['_score'],
**additional_fields)
raw_results.append(result)
+ #psr:初始化facet和拼写建议
facets = {}
spelling_suggestion = None if query_string == suggestion else suggestion
+ #psr:返回搜索结果字典
return {
'results': raw_results,
'hits': hits,
@@ -122,13 +166,17 @@ class ElasticSearchBackend(BaseSearchBackend):
}
+#psr:定义Elasticsearch搜索查询类,继承自BaseSearchQuery
class ElasticSearchQuery(BaseSearchQuery):
+ #psr:转换日期时间格式
def _convert_datetime(self, date):
+ #psr:判断日期是否包含小时信息并格式化
if hasattr(date, 'hour'):
return force_str(date.strftime('%Y%m%d%H%M%S'))
else:
return force_str(date.strftime('%Y%m%d000000'))
+ #psr:清理用户输入的查询字符串
def clean(self, query_fragment):
"""
Provides a mechanism for sanitizing user input before presenting the
@@ -138,13 +186,17 @@ class ElasticSearchQuery(BaseSearchQuery):
to escape reserved characters. Instead, the whole word should be
quoted.
"""
+ #psr:将查询字符串分割为单词
words = query_fragment.split()
cleaned_words = []
+ #psr:遍历每个单词进行清理处理
for word in words:
+ #psr:处理保留字
if word in self.backend.RESERVED_WORDS:
word = word.replace(word, word.lower())
+ #psr:处理保留字符,如果包含则用引号包围
for char in self.backend.RESERVED_CHARACTERS:
if char in word:
word = "'%s'" % word
@@ -152,32 +204,46 @@ class ElasticSearchQuery(BaseSearchQuery):
cleaned_words.append(word)
+ #psr:重新组合清理后的单词
return ' '.join(cleaned_words)
+ #psr:构建查询片段
def build_query_fragment(self, field, filter_type, value):
+ #psr:返回查询字符串
return value.query_string
+ #psr:获取搜索结果数量
def get_count(self):
+ #psr:获取搜索结果并返回长度
results = self.get_results()
return len(results) if results else 0
+ #psr:获取拼写建议
def get_spelling_suggestion(self, preferred_query=None):
+ #psr:返回拼写建议
return self._spelling_suggestion
+ #psr:构建查询参数
def build_params(self, spelling_query=None):
+ #psr:调用父类方法构建参数
kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query)
return kwargs
+#psr:定义Elasticsearch模型搜索表单类,继承自ModelSearchForm
class ElasticSearchModelSearchForm(ModelSearchForm):
+ #psr:执行搜索操作
def search(self):
- # 是否建议搜索
+ #psr:根据表单数据设置是否启用建议功能
self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no"
+ #psr:调用父类搜索方法
sqs = super().search()
return sqs
+#psr:定义Elasticsearch搜索引擎类,继承自BaseEngine
class ElasticSearchEngine(BaseEngine):
+ #psr:指定后端和查询类
backend = ElasticSearchBackend
- query = ElasticSearchQuery
+ query = ElasticSearchQuery
\ No newline at end of file
diff --git a/src/DjangoBlog-master/djangoblog/logentryadmin.py b/src/DjangoBlog-master/djangoblog/logentryadmin.py
index 2f6a5353..dcedb0f8 100644
--- a/src/DjangoBlog-master/djangoblog/logentryadmin.py
+++ b/src/DjangoBlog-master/djangoblog/logentryadmin.py
@@ -1,3 +1,4 @@
+#psr 导入Django管理后台、内容类型、URL处理等相关模块
from django.contrib import admin
from django.contrib.admin.models import DELETION
from django.contrib.contenttypes.models import ContentType
@@ -8,20 +9,25 @@ from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
+#psr 日志条目管理类,用于在Django管理后台中显示和管理管理员操作日志
class LogEntryAdmin(admin.ModelAdmin):
+ #psr 设置列表过滤器,按内容类型过滤日志
list_filter = [
'content_type'
]
+ #psr 设置搜索字段,支持按对象表示和变更消息搜索
search_fields = [
'object_repr',
'change_message'
]
+ #psr 设置列表显示链接字段
list_display_links = [
'action_time',
'get_change_message',
]
+ #psr 设置列表显示字段
list_display = [
'action_time',
'user_link',
@@ -30,25 +36,32 @@ class LogEntryAdmin(admin.ModelAdmin):
'get_change_message',
]
+ #psr 禁止添加权限,日志条目只能由系统自动生成
def has_add_permission(self, request):
return False
+ #psr 设置变更权限,只有超级用户或具有特定权限的用户才能查看,且不能通过POST请求修改
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'
+ #psr 禁止删除权限,日志条目不能被删除
def has_delete_permission(self, request, obj=None):
return False
+ #psr 生成对象链接,将日志中的对象表示转换为可点击的管理页面链接
def object_link(self, obj):
+ #psr 转义对象表示字符串防止XSS攻击
object_link = escape(obj.object_repr)
content_type = obj.content_type
+ #psr 如果不是删除操作且内容类型存在,则尝试生成链接
if obj.action_flag != DELETION and content_type is not None:
# try returning an actual link instead of object repr string
try:
+ #psr 构造管理页面URL并生成HTML链接
url = reverse(
'admin:{}_{}_change'.format(content_type.app_label,
content_type.model),
@@ -57,16 +70,22 @@ class LogEntryAdmin(admin.ModelAdmin):
object_link = '{}'.format(url, object_link)
except NoReverseMatch:
pass
+ #psr 返回安全的HTML链接
return mark_safe(object_link)
+ #psr 设置对象链接字段的排序字段和显示名称
object_link.admin_order_field = 'object_repr'
object_link.short_description = _('object')
+ #psr 生成用户链接,将日志中的用户转换为可点击的管理页面链接
def user_link(self, obj):
+ #psr 获取用户对象的内容类型
content_type = ContentType.objects.get_for_model(type(obj.user))
+ #psr 转义用户字符串防止XSS攻击
user_link = escape(force_str(obj.user))
try:
# try returning an actual link instead of object repr string
+ #psr 构造用户管理页面URL并生成HTML链接
url = reverse(
'admin:{}_{}_change'.format(content_type.app_label,
content_type.model),
@@ -75,15 +94,19 @@ class LogEntryAdmin(admin.ModelAdmin):
user_link = '{}'.format(url, user_link)
except NoReverseMatch:
pass
+ #psr 返回安全的HTML链接
return mark_safe(user_link)
+ #psr 设置用户链接字段的排序字段和显示名称
user_link.admin_order_field = 'user'
user_link.short_description = _('user')
+ #psr 重写查询集,预加载内容类型以提高查询性能
def get_queryset(self, request):
queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type')
+ #psr 重写获取操作方法,移除删除选中项操作
def get_actions(self, request):
actions = super(LogEntryAdmin, self).get_actions(request)
if 'delete_selected' in actions:
diff --git a/src/DjangoBlog-master/djangoblog/settings.py b/src/DjangoBlog-master/djangoblog/settings.py
index cd85eb7f..1593c63f 100644
--- a/src/DjangoBlog-master/djangoblog/settings.py
+++ b/src/DjangoBlog-master/djangoblog/settings.py
@@ -8,40 +8,52 @@ 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/
+#psr Django博客项目的配置文件,包含数据库、缓存、安全、国际化等所有配置项
+#psr 该文件定义了Django应用的核心设置,包括应用列表、中间件、模板、数据库连接等
+#psr 更多信息请参考: https://docs.djangoproject.com/en/1.10/topics/settings/
+#psr 完整配置项列表请参考: https://docs.djangoproject.com/en/1.10/ref/settings/
"""
+#psr 导入操作系统、系统路径、路径处理等相关模块
import os
import sys
from pathlib import Path
+#psr 导入Django国际化翻译模块
from django.utils.translation import gettext_lazy as _
-
+#psr 环境变量转换为布尔值的辅助函数
def env_to_bool(env, default):
str_val = os.environ.get(env)
return default if str_val is None else str_val == 'True'
# Build paths inside the project like this: BASE_DIR / 'subdir'.
+#psr 构建项目内部路径,BASE_DIR指向项目根目录
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!
+#psr Django安全密钥,用于加密签名,生产环境应从环境变量获取
SECRET_KEY = os.environ.get(
'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6'
# SECURITY WARNING: don't run with debug turned on in production!
+#psr 调试模式开关,生产环境应关闭
DEBUG = env_to_bool('DJANGO_DEBUG', True)
# DEBUG = False
+#psr 测试模式标识,用于判断是否在运行测试
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
# ALLOWED_HOSTS = []
+#psr 允许访问的主机列表,*表示允许所有主机
ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']
# django 4.0新增配置
+#psr CSRF信任的源列表,用于跨站请求伪造保护
CSRF_TRUSTED_ORIGINS = ['http://example.com']
# Application definition
-
+#psr 已安装的应用列表,包含Django核心应用和自定义应用
INSTALLED_APPS = [
# 'django.contrib.admin',
'django.contrib.admin.apps.SimpleAdminConfig',
@@ -64,6 +76,7 @@ INSTALLED_APPS = [
'djangoblog'
]
+#psr 中间件配置列表,用于处理请求和响应
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
@@ -81,8 +94,10 @@ MIDDLEWARE = [
'blog.middleware.OnlineMiddleware'
]
+#psr 根URL配置文件
ROOT_URLCONF = 'djangoblog.urls'
+#psr 模板配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@@ -100,12 +115,13 @@ TEMPLATES = [
},
]
+#psr WSGI应用配置
WSGI_APPLICATION = 'djangoblog.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
-
+#psr 数据库配置,使用MySQL数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
@@ -122,6 +138,7 @@ DATABASES = {
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
+#psr 密码验证器配置
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
@@ -137,15 +154,18 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
+#psr 支持的语言列表
LANGUAGES = (
('en', _('English')),
('zh-hans', _('Simplified Chinese')),
('zh-hant', _('Traditional Chinese')),
)
+#psr 本地化文件路径
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
+#psr 语言代码和时区设置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
@@ -159,7 +179,7 @@ USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
-
+#psr Haystack搜索配置,使用Whoosh搜索引擎
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine',
@@ -167,32 +187,41 @@ HAYSTACK_CONNECTIONS = {
},
}
# Automatically update searching index
+#psr 实时更新搜索索引的信号处理器
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# Allow user login with username and password
+#psr 自定义认证后端,支持邮箱或用户名登录
AUTHENTICATION_BACKENDS = [
'accounts.user_login_backend.EmailOrUsernameModelBackend']
STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic')
+#psr 静态文件配置
STATIC_URL = '/static/'
STATICFILES = os.path.join(BASE_DIR, 'static')
+#psr 自定义用户模型和登录URL
AUTH_USER_MODEL = 'accounts.BlogUser'
LOGIN_URL = '/login/'
+#psr 时间格式配置
TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
DATE_TIME_FORMAT = '%Y-%m-%d'
# bootstrap color styles
+#psr Bootstrap颜色样式列表
BOOTSTRAP_COLOR_TYPES = [
'default', 'primary', 'success', 'info', 'warning', 'danger'
]
# paginate
+#psr 分页配置,每页显示10条记录
PAGINATE_BY = 10
# http cache timeout
+#psr HTTP缓存超时时间(秒)
CACHE_CONTROL_MAX_AGE = 2592000
# cache setting
+#psr 缓存配置,使用本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
@@ -201,6 +230,7 @@ CACHES = {
}
}
# 使用redis作为缓存
+#psr 如果配置了Redis URL,则使用Redis作为缓存后端
if os.environ.get("DJANGO_REDIS_URL"):
CACHES = {
'default': {
@@ -209,11 +239,14 @@ if os.environ.get("DJANGO_REDIS_URL"):
}
}
+#psr 站点ID配置
SITE_ID = 1
+#psr 百度搜索引擎通知URL
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:
+#psr 邮件配置,使用SMTP后端
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)
@@ -224,15 +257,19 @@ 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
+#psr 管理员邮箱配置,用于接收错误通知
ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')]
# WX ADMIN password(Two times md5)
+#psr 微信管理员密码(两次MD5加密)
WXADMIN = os.environ.get(
'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7'
+#psr 日志路径配置
LOG_PATH = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOG_PATH):
os.makedirs(LOG_PATH, exist_ok=True)
+#psr 日志配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
@@ -294,16 +331,18 @@ LOGGING = {
}
}
+#psr 静态文件查找器配置
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# other
'compressor.finders.CompressorFinder',
)
+#psr 启用资源压缩
COMPRESS_ENABLED = True
# COMPRESS_OFFLINE = True
-
+#psr CSS和JS压缩过滤器配置
COMPRESS_CSS_FILTERS = [
# creates absolute urls from relative ones
'compressor.filters.css_default.CssAbsoluteFilter',
@@ -314,12 +353,16 @@ COMPRESS_JS_FILTERS = [
'compressor.filters.jsmin.JSMinFilter'
]
+#psr 媒体文件配置
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'
+#psr X-Frame-Options安全头配置
X_FRAME_OPTIONS = 'SAMEORIGIN'
+#psr 默认自动字段配置
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+#psr 如果配置了Elasticsearch主机,则使用Elasticsearch作为搜索引擎
if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
ELASTICSEARCH_DSL = {
'default': {
@@ -333,6 +376,7 @@ if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
}
# Plugin System
+#psr 插件系统配置,定义插件目录和激活的插件列表
PLUGINS_DIR = BASE_DIR / 'plugins'
ACTIVE_PLUGINS = [
'article_copyright',
diff --git a/src/DjangoBlog-master/djangoblog/sitemap.py b/src/DjangoBlog-master/djangoblog/sitemap.py
index 8b7d4460..e9072ca9 100644
--- a/src/DjangoBlog-master/djangoblog/sitemap.py
+++ b/src/DjangoBlog-master/djangoblog/sitemap.py
@@ -1,59 +1,85 @@
+#psr 导入Django站点地图相关模块和项目模型
from django.contrib.sitemaps import Sitemap
from django.urls import reverse
from blog.models import Article, Category, Tag
+#psr 静态页面站点地图类,用于生成网站首页等静态页面的sitemap
class StaticViewSitemap(Sitemap):
+ #psr 设置页面优先级为0.5
priority = 0.5
+ #psr 设置页面更新频率为每日
changefreq = 'daily'
+ #psr 返回需要包含在sitemap中的URL名称列表
def items(self):
return ['blog:index', ]
+ #psr 根据URL名称生成实际的URL路径
def location(self, item):
return reverse(item)
+#psr 文章站点地图类,用于生成已发布文章页面的sitemap
class ArticleSiteMap(Sitemap):
+ #psr 设置文章页面更新频率为每月
changefreq = "monthly"
+ #psr 设置文章页面优先级为0.6
priority = "0.6"
+ #psr 返回所有已发布状态的文章对象
def items(self):
return Article.objects.filter(status='p')
+ #psr 返回文章对象的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
+#psr 分类站点地图类,用于生成文章分类页面的sitemap
class CategorySiteMap(Sitemap):
+ #psr 设置分类页面更新频率为每周
changefreq = "Weekly"
+ #psr 设置分类页面优先级为0.6
priority = "0.6"
+ #psr 返回所有分类对象
def items(self):
return Category.objects.all()
+ #psr 返回分类对象的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
+#psr 标签站点地图类,用于生成文章标签页面的sitemap
class TagSiteMap(Sitemap):
+ #psr 设置标签页面更新频率为每周
changefreq = "Weekly"
+ #psr 设置标签页面优先级为0.3
priority = "0.3"
+ #psr 返回所有标签对象
def items(self):
return Tag.objects.all()
+ #psr 返回标签对象的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
+#psr 用户站点地图类,用于生成用户页面的sitemap
class UserSiteMap(Sitemap):
+ #psr 设置用户页面更新频率为每周
changefreq = "Weekly"
+ #psr 设置用户页面优先级为0.3
priority = "0.3"
+ #psr 返回所有文章作者的去重列表
def items(self):
return list(set(map(lambda x: x.author, Article.objects.all())))
+ #psr 返回用户对象的注册时间
def lastmod(self, obj):
return obj.date_joined
diff --git a/src/DjangoBlog-master/djangoblog/spider_notify.py b/src/DjangoBlog-master/djangoblog/spider_notify.py
index 7b909e96..f49b9c15 100644
--- a/src/DjangoBlog-master/djangoblog/spider_notify.py
+++ b/src/DjangoBlog-master/djangoblog/spider_notify.py
@@ -1,21 +1,31 @@
+#psr 导入日志记录和HTTP请求相关模块
import logging
import requests
from django.conf import settings
+#psr 获取日志记录器实例
logger = logging.getLogger(__name__)
+#psr 蜘蛛通知类,用于向搜索引擎推送网站更新通知
class SpiderNotify():
+ #psr 静态方法,向百度搜索引擎推送URL更新通知
@staticmethod
def baidu_notify(urls):
try:
+ #psr 将URL列表转换为换行分隔的字符串格式
data = '\n'.join(urls)
+ #psr 发送POST请求到百度通知URL
result = requests.post(settings.BAIDU_NOTIFY_URL, data=data)
+ #psr 记录百度返回的响应结果
logger.info(result.text)
except Exception as e:
+ #psr 记录推送过程中出现的异常错误
logger.error(e)
+ #psr 静态方法,通用通知方法,调用百度通知功能
@staticmethod
def notify(url):
+ #psr 调用百度通知方法推送URL
SpiderNotify.baidu_notify(url)
diff --git a/src/DjangoBlog-master/djangoblog/tests.py b/src/DjangoBlog-master/djangoblog/tests.py
index 01237d9a..bd5ad94b 100644
--- a/src/DjangoBlog-master/djangoblog/tests.py
+++ b/src/DjangoBlog-master/djangoblog/tests.py
@@ -1,15 +1,21 @@
+#psr 导入Django测试基类和项目工具函数模块
from django.test import TestCase
from djangoblog.utils import *
+#psr Django博客应用的测试类,用于测试项目中的工具函数
class DjangoBlogTest(TestCase):
+ #psr 测试初始化方法,在每个测试方法执行前运行
def setUp(self):
pass
+ #psr 测试工具函数功能的测试方法
def test_utils(self):
+ #psr 测试SHA256哈希函数,验证字符串'test'的哈希值不为空
md5 = get_sha256('test')
self.assertIsNotNone(md5)
+ #psr 测试Markdown转换功能,验证包含标题、代码块和链接的Markdown文本能正确转换
c = CommonMarkdown.get_markdown('''
# Title1
@@ -24,6 +30,7 @@ class DjangoBlogTest(TestCase):
''')
self.assertIsNotNone(c)
+ #psr 测试字典转URL参数功能,验证字典能正确转换为URL查询字符串
d = {
'd': 'key1',
'd2': 'key2'
diff --git a/src/DjangoBlog-master/djangoblog/urls.py b/src/DjangoBlog-master/djangoblog/urls.py
index 4aae58a6..65a915a8 100644
--- a/src/DjangoBlog-master/djangoblog/urls.py
+++ b/src/DjangoBlog-master/djangoblog/urls.py
@@ -1,6 +1,7 @@
+#psr Django博客项目的URL配置文件,定义了整个应用的路由映射关系
"""djangoblog URL Configuration
-The `urlpatterns` list routes URLs to views. For more information please see:
+The [urlpatterns](file://d:\PSR_projects\src\PSR_pro01\urls.py#L19-L21) list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
@@ -13,6 +14,7 @@ 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'))
"""
+#psr 导入Django配置、国际化、静态文件、站点地图等相关模块
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static
@@ -21,12 +23,14 @@ from django.urls import path, include
from django.urls import re_path
from haystack.views import search_view_factory
+#psr 导入自定义视图、管理站点、搜索表单和Feed类
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
+#psr 定义站点地图配置,包含文章、分类、标签、用户和静态页面的sitemap
sitemaps = {
'blog': ArticleSiteMap,
@@ -36,29 +40,44 @@ sitemaps = {
'static': StaticViewSitemap
}
+#psr 定义自定义错误处理视图
handler404 = 'blog.views.page_not_found_view'
handler500 = 'blog.views.server_error_view'
handle403 = 'blog.views.permission_denied_view'
+#psr 定义基础URL模式,包含国际化URL
urlpatterns = [
path('i18n/', include('django.conf.urls.i18n')),
]
+#psr 使用国际化模式定义主要URL路由
urlpatterns += i18n_patterns(
+ #psr 管理后台路由
re_path(r'^admin/', admin_site.urls),
+ #psr 博客应用路由
re_path(r'', include('blog.urls', namespace='blog')),
+ #psr Markdown编辑器路由
re_path(r'mdeditor/', include('mdeditor.urls')),
+ #psr 评论应用路由
re_path(r'', include('comments.urls', namespace='comment')),
+ #psr 账户应用路由
re_path(r'', include('accounts.urls', namespace='account')),
+ #psr OAuth认证路由
re_path(r'', include('oauth.urls', namespace='oauth')),
+ #psr 站点地图路由
re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
+ #psr RSS Feed路由
re_path(r'^feed/$', DjangoBlogFeed()),
re_path(r'^rss/$', DjangoBlogFeed()),
+ #psr 搜索路由,使用Elasticsearch搜索视图
re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm),
name='search'),
+ #psr 服务器管理路由
re_path(r'', include('servermanager.urls', namespace='servermanager')),
+ #psr OwnTracks位置追踪路由
re_path(r'', include('owntracks.urls', namespace='owntracks'))
, prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+#psr 在调试模式下添加媒体文件路由
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
diff --git a/src/DjangoBlog-master/djangoblog/utils.py b/src/DjangoBlog-master/djangoblog/utils.py
index 57f63dca..51ff0fd9 100644
--- a/src/DjangoBlog-master/djangoblog/utils.py
+++ b/src/DjangoBlog-master/djangoblog/utils.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# encoding: utf-8
-
+#psr:导入日志、操作系统、随机数、字符串、UUID、哈希等相关标准库模块
import logging
import os
import random
@@ -9,6 +9,7 @@ import string
import uuid
from hashlib import sha256
+#psr:导入第三方库和Django相关模块
import bleach
import markdown
import requests
@@ -17,45 +18,59 @@ from django.contrib.sites.models import Site
from django.core.cache import cache
from django.templatetags.static import static
+#psr:获取日志记录器实例
logger = logging.getLogger(__name__)
+#psr:获取最新的文章ID和评论ID
def get_max_articleid_commentid():
from blog.models import Article
from comments.models import Comment
+ #psr:返回最新文章和评论的主键值
return (Article.objects.latest().pk, Comment.objects.latest().pk)
+#psr:计算字符串的SHA256哈希值
def get_sha256(str):
+ #psr:创建SHA256哈希对象并计算摘要
m = sha256(str.encode('utf-8'))
return m.hexdigest()
+#psr:缓存装饰器,用于缓存函数执行结果
def cache_decorator(expiration=3 * 60):
+ #psr:装饰器包装函数
def wrapper(func):
+ #psr:新的函数实现缓存逻辑
def news(*args, **kwargs):
try:
+ #psr:尝试从视图获取缓存键
view = args[0]
key = view.get_cache_key()
except:
key = None
+ #psr:如果无法获取缓存键则生成唯一键
if not key:
unique_str = repr((func, args, kwargs))
m = sha256(unique_str.encode('utf-8'))
key = m.hexdigest()
+ #psr:从缓存中获取值
value = cache.get(key)
if value is not None:
# logger.info('cache_decorator get cache:%s key:%s' % (func.__name__, key))
+ #psr:处理默认缓存值
if str(value) == '__default_cache_value__':
return None
else:
return value
else:
+ #psr:缓存未命中,执行函数并设置缓存
logger.debug(
'cache_decorator set cache:%s key:%s' %
(func.__name__, key))
value = func(*args, **kwargs)
+ #psr:根据函数返回值设置缓存
if value is None:
cache.set(key, '__default_cache_value__', expiration)
else:
@@ -67,6 +82,7 @@ def cache_decorator(expiration=3 * 60):
return wrapper
+#psr:刷新视图缓存
def expire_view_cache(path, servername, serverport, key_prefix=None):
'''
刷新视图缓存
@@ -76,13 +92,16 @@ def expire_view_cache(path, servername, serverport, key_prefix=None):
:param key_prefix:前缀
:return:是否成功
'''
+ #psr:导入Django HTTP请求和缓存相关模块
from django.http import HttpRequest
from django.utils.cache import get_cache_key
+ #psr:创建HTTP请求对象并设置相关属性
request = HttpRequest()
request.META = {'SERVER_NAME': servername, 'SERVER_PORT': serverport}
request.path = path
+ #psr:获取缓存键并删除对应缓存
key = get_cache_key(request, key_prefix=key_prefix, cache=cache)
if key:
logger.info('expire_view_cache:get key:{path}'.format(path=path))
@@ -92,15 +111,20 @@ def expire_view_cache(path, servername, serverport, key_prefix=None):
return False
+#psr:使用缓存装饰器缓存当前站点信息
@cache_decorator()
def get_current_site():
+ #psr:获取当前站点对象
site = Site.objects.get_current()
return site
+#psr:Markdown处理工具类
class CommonMarkdown:
+ #psr:静态方法,转换Markdown文本为HTML并生成目录
@staticmethod
def _convert_markdown(value):
+ #psr:创建Markdown处理器并配置扩展
md = markdown.Markdown(
extensions=[
'extra',
@@ -109,23 +133,30 @@ class CommonMarkdown:
'tables',
]
)
+ #psr:转换Markdown并获取正文和目录
body = md.convert(value)
toc = md.toc
return body, toc
+ #psr:静态方法,获取带目录的Markdown HTML
@staticmethod
def get_markdown_with_toc(value):
+ #psr:调用转换方法获取正文和目录
body, toc = CommonMarkdown._convert_markdown(value)
return body, toc
+ #psr:静态方法,获取不带目录的Markdown HTML
@staticmethod
def get_markdown(value):
+ #psr:调用转换方法只返回正文
body, toc = CommonMarkdown._convert_markdown(value)
return body
+#psr:发送邮件函数
def send_email(emailto, title, content):
from djangoblog.blog_signals import send_email_signal
+ #psr:发送邮件信号
send_email_signal.send(
send_email.__class__,
emailto=emailto,
@@ -133,25 +164,33 @@ def send_email(emailto, title, content):
content=content)
+#psr:生成6位随机数字验证码
def generate_code() -> str:
"""生成随机数验证码"""
+ #psr:从数字中随机采样6个字符组成验证码
return ''.join(random.sample(string.digits, 6))
+#psr:将字典转换为URL查询字符串
def parse_dict_to_url(dict):
from urllib.parse import quote
+ #psr:对字典键值对进行URL编码并拼接
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))
for k, v in dict.items()])
return url
+#psr:获取博客设置信息
def get_blog_setting():
+ #psr:先从缓存中获取设置
value = cache.get('get_blog_setting')
if value:
return value
else:
+ #psr:缓存未命中则从数据库获取或创建默认设置
from blog.models import BlogSettings
if not BlogSettings.objects.count():
+ #psr:创建默认博客设置
setting = BlogSettings()
setting.site_name = 'djangoblog'
setting.site_description = '基于Django的博客系统'
@@ -167,12 +206,14 @@ def get_blog_setting():
setting.show_gongan_code = False
setting.comment_need_review = False
setting.save()
+ #psr:获取第一个设置对象并缓存
value = BlogSettings.objects.first()
logger.info('set cache get_blog_setting')
cache.set('get_blog_setting', value)
return value
+#psr:保存用户头像到本地
def save_user_avatar(url):
'''
保存用户头像
@@ -182,40 +223,54 @@ def save_user_avatar(url):
logger.info(url)
try:
+ #psr:设置头像保存基础目录
basedir = os.path.join(settings.STATICFILES, 'avatar')
+ #psr:下载头像图片
rsp = requests.get(url, timeout=2)
if rsp.status_code == 200:
+ #psr:创建目录如果不存在
if not os.path.exists(basedir):
os.makedirs(basedir)
+ #psr:判断是否为图片文件并获取扩展名
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'
+ #psr:生成唯一文件名并保存文件
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)
+ #psr:返回静态文件路径
return static('avatar/' + save_filename)
except Exception as e:
+ #psr:处理异常并返回默认头像
logger.error(e)
return static('blog/img/avatar.png')
+#psr:删除侧边栏缓存
def delete_sidebar_cache():
from blog.models import LinkShowType
+ #psr:生成侧边栏缓存键列表
keys = ["sidebar" + x for x in LinkShowType.values]
+ #psr:遍历删除每个缓存键
for k in keys:
logger.info('delete sidebar key:' + k)
cache.delete(k)
+#psr:删除模板片段缓存
def delete_view_cache(prefix, keys):
from django.core.cache.utils import make_template_fragment_key
+ #psr:生成模板片段缓存键并删除
key = make_template_fragment_key(prefix, keys)
cache.delete(key)
+#psr:获取资源URL路径
def get_resource_url():
+ #psr:优先使用配置的静态URL,否则根据当前站点生成
if settings.STATIC_URL:
return settings.STATIC_URL
else:
@@ -223,10 +278,14 @@ def get_resource_url():
return 'http://' + site.domain + '/static/'
+#psr:定义允许的HTML标签
ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1',
'h2', 'p']
+#psr:定义允许的HTML属性
ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
+#psr:清理HTML内容,只保留允许的标签和属性
def sanitize_html(html):
- return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)
+ #psr:使用bleach库清理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
index 04e3f7fd..375057e6 100644
--- a/src/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
+++ b/src/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
@@ -1,7 +1,9 @@
# encoding: utf-8
+# Handle minimum requirement.
from __future__ import absolute_import, division, print_function, unicode_literals
+# Handle minimum requirement.
import json
import os
import re
@@ -9,6 +11,7 @@ import shutil
import threading
import warnings
+# Handle minimum requirement.
import six
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
@@ -34,6 +37,7 @@ from whoosh.qparser import QueryParser
from whoosh.searching import ResultsPage
from whoosh.writing import AsyncWriter
+#psr:尝试导入whoosh库,如果失败,则抛出依赖缺失异常
try:
import whoosh
except ImportError:
@@ -41,29 +45,33 @@ except ImportError:
"The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.")
# Handle minimum requirement.
+#psr:检查whoosh版本要求,必须大于等于2.5.0
if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):
raise MissingDependency(
"The 'whoosh' backend requires version 2.5.0 or greater.")
# Bubble up the correct error.
-
+#psr:定义日期时间正则表达式模式
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?)?$')
+#psr:创建线程本地存储对象
LOCALS = threading.local()
LOCALS.RAM_STORE = None
-
+#psr:自定义HTML格式化器类,用于高亮显示搜索结果
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.
"""
+ #psr:定义HTML模板格式
template = '<%(tag)s>%(t)s%(tag)s>'
-
+#psr:定义Whoosh搜索后端类,继承自BaseSearchBackend
class WhooshSearchBackend(BaseSearchBackend):
# Word reserved by Whoosh for special use.
+ #psr:定义Whoosh保留关键字
RESERVED_WORDS = (
'AND',
'NOT',
@@ -74,35 +82,45 @@ class WhooshSearchBackend(BaseSearchBackend):
# Characters reserved by Whoosh for special use.
# The '\\' must come first, so as not to overwrite the other slash
# replacements.
+ #psr:定义Whoosh保留字符
RESERVED_CHARACTERS = (
'\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}',
'[', ']', '^', '"', '~', '*', '?', ':', '.',
)
+ #psr:初始化Whoosh搜索后端
def __init__(self, connection_alias, **connection_options):
super(
WhooshSearchBackend,
self).__init__(
connection_alias,
**connection_options)
+ #psr:标记设置是否完成
self.setup_complete = False
+ #psr:是否使用文件存储
self.use_file_storage = True
+ #psr:POST请求限制大小
self.post_limit = getattr(
connection_options,
'POST_LIMIT',
128 * 1024 * 1024)
+ #psr:索引文件路径
self.path = connection_options.get('PATH')
+ #psr:根据配置决定存储类型
if connection_options.get('STORAGE', 'file') != 'file':
self.use_file_storage = False
-
+
+ #psr:验证文件存储配置
if self.use_file_storage and not self.path:
raise ImproperlyConfigured(
"You must specify a 'PATH' in your settings for connection '%s'." %
connection_alias)
+ #psr:获取日志记录器
self.log = logging.getLogger('haystack')
+ #psr:设置Whoosh索引环境
def setup(self):
"""
Defers loading until needed.
@@ -111,15 +129,18 @@ class WhooshSearchBackend(BaseSearchBackend):
new_index = False
# Make sure the index is there.
+ #psr:创建索引目录如果不存在
if self.use_file_storage and not os.path.exists(self.path):
os.makedirs(self.path)
new_index = True
+ #psr:检查索引目录写入权限
if self.use_file_storage and not os.access(self.path, os.W_OK):
raise IOError(
"The path to your Whoosh index '%s' is not writable for the current user/group." %
self.path)
+ #psr:根据配置选择存储方式
if self.use_file_storage:
self.storage = FileStorage(self.path)
else:
@@ -130,10 +151,12 @@ class WhooshSearchBackend(BaseSearchBackend):
self.storage = LOCALS.RAM_STORE
+ #psr:构建索引模式和解析器
self.content_field_name, self.schema = self.build_schema(
connections[self.connection_alias].get_unified_index().all_searchfields())
self.parser = QueryParser(self.content_field_name, schema=self.schema)
+ #psr:创建或打开索引
if new_index is True:
self.index = self.storage.create_index(self.schema)
else:
@@ -142,9 +165,12 @@ class WhooshSearchBackend(BaseSearchBackend):
except index.EmptyIndexError:
self.index = self.storage.create_index(self.schema)
+ #psr:标记设置完成
self.setup_complete = True
+ #psr:构建Whoosh索引模式
def build_schema(self, fields):
+ #psr:定义基本模式字段
schema_fields = {
ID: WHOOSH_ID(stored=True, unique=True),
DJANGO_CT: WHOOSH_ID(stored=True),
@@ -152,11 +178,14 @@ class WhooshSearchBackend(BaseSearchBackend):
}
# Grab the number of keys that are hard-coded into Haystack.
# We'll use this to (possibly) fail slightly more gracefully later.
+ #psr:获取初始键数量
initial_key_count = len(schema_fields)
content_field_name = ''
+ #psr:根据字段类型构建相应的模式字段
for field_name, field_class in fields.items():
if field_class.is_multivalued:
+ #psr:处理多值字段
if field_class.indexed is False:
schema_fields[field_class.index_fieldname] = IDLIST(
stored=True, field_boost=field_class.boost)
@@ -164,48 +193,62 @@ class WhooshSearchBackend(BaseSearchBackend):
schema_fields[field_class.index_fieldname] = KEYWORD(
stored=True, commas=True, scorable=True, field_boost=field_class.boost)
elif field_class.field_type in ['date', 'datetime']:
+ #psr:处理日期时间字段
schema_fields[field_class.index_fieldname] = DATETIME(
stored=field_class.stored, sortable=True)
elif field_class.field_type == 'integer':
+ #psr:处理整数字段
schema_fields[field_class.index_fieldname] = NUMERIC(
stored=field_class.stored, numtype=int, field_boost=field_class.boost)
elif field_class.field_type == 'float':
+ #psr:处理浮点数字段
schema_fields[field_class.index_fieldname] = NUMERIC(
stored=field_class.stored, numtype=float, field_boost=field_class.boost)
elif field_class.field_type == 'boolean':
+ #psr:处理布尔字段
# Field boost isn't supported on BOOLEAN as of 1.8.2.
schema_fields[field_class.index_fieldname] = BOOLEAN(
stored=field_class.stored)
elif field_class.field_type == 'ngram':
+ #psr:处理ngram字段
schema_fields[field_class.index_fieldname] = NGRAM(
minsize=3, maxsize=15, stored=field_class.stored, field_boost=field_class.boost)
elif field_class.field_type == 'edge_ngram':
+ #psr:处理边缘ngram字段
schema_fields[field_class.index_fieldname] = NGRAMWORDS(minsize=2, maxsize=15, at='start',
stored=field_class.stored,
field_boost=field_class.boost)
else:
+ #psr:处理文本字段,使用中文分析器
# 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)
+ #psr:处理文档字段
if field_class.document is True:
content_field_name = field_class.index_fieldname
schema_fields[field_class.index_fieldname].spelling = True
# Fail more gracefully than relying on the backend to die if no fields
# are found.
+ #psr:验证是否有字段被添加到模式中
if len(schema_fields) <= initial_key_count:
raise SearchBackendError(
"No fields were found in any search_indexes. Please correct this before attempting to search.")
+ #psr:返回内容字段名和构建好的模式
return (content_field_name, Schema(**schema_fields))
+ #psr:更新索引中的文档
def update(self, index, iterable, commit=True):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
+ #psr:刷新索引并创建异步写入器
self.index = self.index.refresh()
writer = AsyncWriter(self.index)
+ #psr:遍历可迭代对象准备并更新文档
for obj in iterable:
try:
doc = index.full_prepare(obj)
@@ -214,22 +257,27 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
# Really make sure it's unicode, because Whoosh won't have it any
# other way.
+ #psr:确保文档内容都是unicode格式
for key in doc:
doc[key] = self._from_python(doc[key])
# Document boosts aren't supported in Whoosh 2.5.0+.
+ #psr:删除文档提升字段(Whoosh 2.5.0+不支持)
if 'boost' in doc:
del doc['boost']
try:
+ #psr:更新文档
writer.update_document(**doc)
except Exception as e:
+ #psr:处理异常情况
if not self.silently_fail:
raise
# We'll log the object identifier but won't include the actual object
# to avoid the possibility of that generating encoding errors while
# processing the log message:
+ #psr:记录错误日志
self.log.error(
u"%s while preparing object for update" %
e.__class__.__name__,
@@ -239,43 +287,54 @@ class WhooshSearchBackend(BaseSearchBackend):
"index": index,
"object": get_identifier(obj)}})
+ #psr:如果有数据则提交更改
if len(iterable) > 0:
# For now, commit no matter what, as we run into locking issues
# otherwise.
writer.commit()
+ #psr:从索引中移除对象
def remove(self, obj_or_string, commit=True):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
+ #psr:刷新索引并获取对象标识符
self.index = self.index.refresh()
whoosh_id = get_identifier(obj_or_string)
try:
+ #psr:通过查询删除文档
self.index.delete_by_query(
q=self.parser.parse(
- u'%s:"%s"' %
- (ID, whoosh_id)))
+ u'%s:"%s"' % (ID, whoosh_id)))
except Exception as e:
+ #psr:处理异常情况
if not self.silently_fail:
raise
+ #psr:记录错误日志
self.log.error(
"Failed to remove document '%s' from Whoosh: %s",
whoosh_id,
e,
exc_info=True)
+ #psr:清空索引
def clear(self, models=None, commit=True):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
+ #psr:刷新索引
self.index = self.index.refresh()
+ #psr:验证模型参数
if models is not None:
assert isinstance(models, (list, tuple))
try:
+ #psr:根据参数决定是删除整个索引还是特定模型
if models is None:
self.delete_index()
else:
@@ -283,16 +342,17 @@ class WhooshSearchBackend(BaseSearchBackend):
for model in models:
models_to_delete.append(
- u"%s:%s" %
- (DJANGO_CT, get_model_ct(model)))
+ u"%s:%s" % (DJANGO_CT, get_model_ct(model)))
self.index.delete_by_query(
q=self.parser.parse(
u" OR ".join(models_to_delete)))
except Exception as e:
+ #psr:处理异常情况
if not self.silently_fail:
raise
+ #psr:记录错误日志
if models is not None:
self.log.error(
"Failed to clear Whoosh index of models '%s': %s",
@@ -303,31 +363,34 @@ class WhooshSearchBackend(BaseSearchBackend):
self.log.error(
"Failed to clear Whoosh index: %s", e, exc_info=True)
+ #psr:删除整个索引
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.
+ #psr:根据存储类型删除索引文件
if self.use_file_storage and os.path.exists(self.path):
shutil.rmtree(self.path)
elif not self.use_file_storage:
self.storage.clean()
- # Recreate everything.
+ #psr:重新创建索引环境
self.setup()
+ #psr:优化索引
def optimize(self):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
+ #psr:刷新索引并执行优化
self.index = self.index.refresh()
self.index.optimize()
+ #psr:计算分页参数
def calculate_page(self, start_offset=0, end_offset=None):
- # Prevent against Whoosh throwing an error. Requires an end_offset
- # greater than 0.
+ #psr:防止Whoosh抛出错误,确保结束偏移量大于0
if end_offset is not None and end_offset <= 0:
end_offset = 1
- # Determine the page.
+ #psr:确定页码
page_num = 0
if end_offset is None:
@@ -341,10 +404,11 @@ class WhooshSearchBackend(BaseSearchBackend):
if page_length and page_length > 0:
page_num = int(start_offset / page_length)
- # Increment because Whoosh uses 1-based page numbers.
+ #psr:增加页码因为Whoosh使用1基页码
page_num += 1
return page_num, page_length
+ #psr:执行搜索查询
@log_query
def search(
self,
@@ -366,10 +430,11 @@ class WhooshSearchBackend(BaseSearchBackend):
limit_to_registered_models=None,
result_class=None,
**kwargs):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
- # A zero length query should return no results.
+ #psr:处理零长度查询
if len(query_string) == 0:
return {
'results': [],
@@ -378,8 +443,7 @@ class WhooshSearchBackend(BaseSearchBackend):
query_string = force_str(query_string)
- # A one-character query (non-wildcard) gets nabbed by a stopwords
- # filter and should yield zero results.
+ #psr:处理单字符查询
if len(query_string) <= 1 and query_string != u'*':
return {
'results': [],
@@ -388,6 +452,7 @@ class WhooshSearchBackend(BaseSearchBackend):
reverse = False
+ #psr:处理排序参数
if sort_by is not None:
# Determine if we need to reverse the results and if Whoosh can
# handle what it's being asked to sort by. Reversing is an
@@ -417,6 +482,7 @@ class WhooshSearchBackend(BaseSearchBackend):
sort_by = sort_by_list[0]
+ #psr:处理分面搜索警告
if facets is not None:
warnings.warn(
"Whoosh does not handle faceting.",
@@ -438,6 +504,7 @@ class WhooshSearchBackend(BaseSearchBackend):
narrowed_results = None
self.index = self.index.refresh()
+ #psr:处理模型限制参数
if limit_to_registered_models is None:
limit_to_registered_models = getattr(
settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
@@ -451,6 +518,7 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
model_choices = []
+ #psr:构建模型选择查询
if len(model_choices) > 0:
if narrow_queries is None:
narrow_queries = set()
@@ -460,6 +528,7 @@ class WhooshSearchBackend(BaseSearchBackend):
narrow_searcher = None
+ #psr:处理窄化查询
if narrow_queries is not None:
# Potentially expensive? I don't see another way to do it in
# Whoosh...
@@ -482,6 +551,7 @@ class WhooshSearchBackend(BaseSearchBackend):
self.index = self.index.refresh()
+ #psr:执行实际搜索
if self.index.doc_count():
searcher = self.index.searcher()
parsed_query = self.parser.parse(query_string)
@@ -496,6 +566,7 @@ class WhooshSearchBackend(BaseSearchBackend):
page_num, page_length = self.calculate_page(
start_offset, end_offset)
+ #psr:设置搜索参数
search_kwargs = {
'pagelen': page_length,
'sortedby': sort_by,
@@ -507,12 +578,14 @@ class WhooshSearchBackend(BaseSearchBackend):
search_kwargs['filter'] = narrowed_results
try:
+ #psr:执行搜索页面查询
raw_page = searcher.search_page(
parsed_query,
page_num,
**search_kwargs
)
except ValueError:
+ #psr:处理数值错误异常
if not self.silently_fail:
raise
@@ -531,6 +604,7 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': None,
}
+ #psr:处理搜索结果
results = self._process_results(
raw_page,
highlight=highlight,
@@ -544,6 +618,7 @@ class WhooshSearchBackend(BaseSearchBackend):
return results
else:
+ #psr:处理空索引情况
if self.include_spelling:
if spelling_query:
spelling_suggestion = self.create_spelling_suggestion(
@@ -560,6 +635,7 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': spelling_suggestion,
}
+ #psr:相似文档搜索
def more_like_this(
self,
model_instance,
@@ -570,6 +646,7 @@ class WhooshSearchBackend(BaseSearchBackend):
limit_to_registered_models=None,
result_class=None,
**kwargs):
+ #psr:如果设置未完成则先进行设置
if not self.setup_complete:
self.setup()
@@ -582,6 +659,7 @@ class WhooshSearchBackend(BaseSearchBackend):
narrowed_results = None
self.index = self.index.refresh()
+ #psr:处理模型限制参数
if limit_to_registered_models is None:
limit_to_registered_models = getattr(
settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
@@ -595,6 +673,7 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
model_choices = []
+ #psr:构建模型选择查询
if len(model_choices) > 0:
if narrow_queries is None:
narrow_queries = set()
@@ -602,11 +681,13 @@ class WhooshSearchBackend(BaseSearchBackend):
narrow_queries.add(' OR '.join(
['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
+ #psr:添加附加查询字符串
if additional_query_string and additional_query_string != '*':
narrow_queries.add(additional_query_string)
narrow_searcher = None
+ #psr:处理窄化查询
if narrow_queries is not None:
# Potentially expensive? I don't see another way to do it in
# Whoosh...
@@ -627,11 +708,13 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
narrowed_results = recent_narrowed_results
+ #psr:计算分页参数
page_num, page_length = self.calculate_page(start_offset, end_offset)
self.index = self.index.refresh()
raw_results = EmptyResults()
+ #psr:执行相似文档搜索
if self.index.doc_count():
query = "%s:%s" % (ID, get_identifier(model_instance))
searcher = self.index.searcher()
@@ -647,8 +730,10 @@ class WhooshSearchBackend(BaseSearchBackend):
raw_results.filter(narrowed_results)
try:
+ #psr:创建结果页面
raw_page = ResultsPage(raw_results, page_num, page_length)
except ValueError:
+ #psr:处理数值错误异常
if not self.silently_fail:
raise
@@ -667,6 +752,7 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': None,
}
+ #psr:处理搜索结果
results = self._process_results(raw_page, result_class=result_class)
searcher.close()
@@ -675,6 +761,7 @@ class WhooshSearchBackend(BaseSearchBackend):
return results
+ #psr:处理搜索结果
def _process_results(
self,
raw_page,
@@ -689,6 +776,7 @@ class WhooshSearchBackend(BaseSearchBackend):
# can cause pagination failures.
hits = len(raw_page)
+ #psr:设置结果类
if result_class is None:
result_class = SearchResult
@@ -697,6 +785,7 @@ class WhooshSearchBackend(BaseSearchBackend):
unified_index = connections[self.connection_alias].get_unified_index()
indexed_models = unified_index.get_indexed_models()
+ #psr:遍历原始结果处理每条记录
for doc_offset, raw_result in enumerate(raw_page):
score = raw_page.score(doc_offset) or 0
app_label, model_name = raw_result[DJANGO_CT].split('.')
@@ -704,6 +793,7 @@ class WhooshSearchBackend(BaseSearchBackend):
model = haystack_get_model(app_label, model_name)
if model and model in indexed_models:
+ #psr:处理字段数据
for key, value in raw_result.items():
index = unified_index.get_index(model)
string_key = str(key)
@@ -723,9 +813,11 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
additional_fields[string_key] = self._to_python(value)
+ #psr:删除不需要的字段
del (additional_fields[DJANGO_CT])
del (additional_fields[DJANGO_ID])
+ #psr:处理高亮显示
if highlight:
sa = StemmingAnalyzer()
formatter = WhooshHtmlFormatter('em')
@@ -742,6 +834,7 @@ class WhooshSearchBackend(BaseSearchBackend):
self.content_field_name: [whoosh_result],
}
+ #psr:创建搜索结果对象
result = result_class(
app_label,
model_name,
@@ -752,6 +845,7 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
hits -= 1
+ #psr:处理拼写建议
if self.include_spelling:
if spelling_query:
spelling_suggestion = self.create_spelling_suggestion(
@@ -760,6 +854,7 @@ class WhooshSearchBackend(BaseSearchBackend):
spelling_suggestion = self.create_spelling_suggestion(
query_string)
+ #psr:返回处理后的结果
return {
'results': results,
'hits': hits,
@@ -767,6 +862,7 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': spelling_suggestion,
}
+ #psr:创建拼写建议
def create_spelling_suggestion(self, query_string):
spelling_suggestion = None
reader = self.index.reader()
@@ -787,6 +883,7 @@ class WhooshSearchBackend(BaseSearchBackend):
query_words = cleaned_query.split()
suggested_words = []
+ #psr:为每个查询词生成建议
for word in query_words:
suggestions = corrector.suggest(word, limit=1)
@@ -796,6 +893,7 @@ class WhooshSearchBackend(BaseSearchBackend):
spelling_suggestion = ' '.join(suggested_words)
return spelling_suggestion
+ #psr:将Python值转换为Whoosh字符串
def _from_python(self, value):
"""
Converts Python values to a string for Whoosh.
@@ -819,6 +917,7 @@ class WhooshSearchBackend(BaseSearchBackend):
value = force_str(value)
return value
+ #psr:将Whoosh值转换为Python原生值
def _to_python(self, value):
"""
Converts values from Whoosh to native Python values.
@@ -870,13 +969,16 @@ class WhooshSearchBackend(BaseSearchBackend):
return value
+#psr:定义Whoosh搜索查询类,继承自BaseSearchQuery
class WhooshSearchQuery(BaseSearchQuery):
+ #psr:转换日期时间格式
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'))
+ #psr:清理查询片段
def clean(self, query_fragment):
"""
Provides a mechanism for sanitizing user input before presenting the
@@ -902,11 +1004,13 @@ class WhooshSearchQuery(BaseSearchQuery):
return ' '.join(cleaned_words)
+ #psr:构建查询片段
def build_query_fragment(self, field, filter_type, value):
from haystack import connections
query_frag = ''
is_datetime = False
+ #psr:处理不同类型的值
if not hasattr(value, 'input_type_name'):
# Handle when we've got a ``ValuesListQuerySet``...
if hasattr(value, 'values_list'):
@@ -936,6 +1040,7 @@ class WhooshSearchQuery(BaseSearchQuery):
index_fieldname = u'%s:' % connections[self._using].get_unified_index(
).get_index_fieldname(field)
+ #psr:定义过滤类型映射
filter_types = {
'content': '%s',
'contains': '*%s*',
@@ -949,6 +1054,7 @@ class WhooshSearchQuery(BaseSearchQuery):
'fuzzy': u'%s~',
}
+ #psr:根据不同过滤类型构建查询片段
if value.post_process is False:
query_frag = prepared_value
else:
@@ -1026,19 +1132,15 @@ class WhooshSearchQuery(BaseSearchQuery):
query_frag = filter_types[filter_type] % prepared_value
+ #psr:格式化查询片段
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)
-
-
+#psr:定义Whoosh搜索引擎类,继承自BaseEngine
class WhooshEngine(BaseEngine):
+ #psr:指定后端和查询类
backend = WhooshSearchBackend
query = WhooshSearchQuery
diff --git a/src/DjangoBlog-master/djangoblog/wsgi.py b/src/DjangoBlog-master/djangoblog/wsgi.py
index 2295efd5..ea2fefee 100644
--- a/src/DjangoBlog-master/djangoblog/wsgi.py
+++ b/src/DjangoBlog-master/djangoblog/wsgi.py
@@ -1,8 +1,11 @@
"""
+ #psr:WSGI配置文件,用于部署Django应用程序
WSGI config for djangoblog project.
+#psr:该文件将WSGI可调用对象暴露为模块级变量application
It exposes the WSGI callable as a module-level variable named ``application``.
+#psr:更多信息参考Django官方文档中的部署指南
For more information on this file, see
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
"""
@@ -11,6 +14,8 @@ import os
from django.core.wsgi import get_wsgi_application
+#psr:设置默认的Django设置模块环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
+#psr:获取WSGI应用程序实例,用于处理HTTP请求
application = get_wsgi_application()