diangoblog部分注释源码

develop
施周易 4 months ago
parent ef99f6daae
commit 4cffd2c7ff

@ -1 +1,2 @@
# szy:此文件用于将当前目录识别为一个Python包
default_app_config = 'djangoblog.apps.DjangoblogAppConfig'

@ -1,3 +1,4 @@
# szy功能描述自定义Django后台管理站点并注册各个模型
from django.contrib.admin import AdminSite
from django.contrib.admin.models import LogEntry
from django.contrib.sites.admin import SiteAdmin
@ -16,14 +17,16 @@ from owntracks.models import *
from servermanager.admin import *
from servermanager.models import *
# szy自定义Django后台管理站点并注册各个模型
class DjangoBlogAdminSite(AdminSite):
site_header = 'djangoblog administration'
site_title = 'djangoblog site admin'
# szy初始化管理站点设置站点名称
def __init__(self, name='admin'):
super().__init__(name)
# szy检查用户权限是否为超级管理员
def has_permission(self, request):
return request.user.is_superuser
@ -37,7 +40,7 @@ class DjangoBlogAdminSite(AdminSite):
# ]
# return urls + my_urls
# szy注册各个模型到后台管理
admin_site = DjangoBlogAdminSite(name='admin')
admin_site.register(Article, ArticlelAdmin)

@ -1,9 +1,11 @@
from django.apps import AppConfig
# szyDjango应用配置类用于加载插件
class DjangoblogAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'djangoblog'
# szy应用准备时加载插件
def ready(self):
super().ready()
# Import and load plugins here

@ -1,3 +1,4 @@
# szy定义Django信号并处理相关业务逻辑
import _thread
import logging
@ -22,7 +23,7 @@ oauth_user_login_signal = django.dispatch.Signal(['id'])
send_email_signal = django.dispatch.Signal(
['emailto', 'title', 'content'])
# szy处理发送邮件的信号
@receiver(send_email_signal)
def send_email_signal_handler(sender, **kwargs):
emailto = kwargs['emailto']
@ -50,7 +51,7 @@ def send_email_signal_handler(sender, **kwargs):
log.send_result = False
log.save()
# szy处理OAuth用户登录信号
@receiver(oauth_user_login_signal)
def oauth_user_login_signal_handler(sender, **kwargs):
id = kwargs['id']

@ -10,7 +10,7 @@ from blog.models import Article
logger = logging.getLogger(__name__)
# szy定义Elasticsearch后端处理索引和查询
class ElasticSearchBackend(BaseSearchBackend):
def __init__(self, connection_alias, **connection_options):
super(
@ -21,38 +21,46 @@ class ElasticSearchBackend(BaseSearchBackend):
self.manager = ArticleDocumentManager()
self.include_spelling = True
# szy获取要索引的模型数据
def _get_models(self, iterable):
models = iterable if iterable and iterable[0] else Article.objects.all()
docs = self.manager.convert_to_doc(models)
return docs
# szy创建索引
def _create(self, models):
self.manager.create_index()
docs = self._get_models(models)
self.manager.rebuild(docs)
# szy删除索引
def _delete(self, models):
for m in models:
m.delete()
return True
# szy重组索引
def _rebuild(self, models):
models = models if models else Article.objects.all()
docs = self.manager.convert_to_doc(models)
self.manager.update_docs(docs)
# szy更新索引
def update(self, index, iterable, commit=True):
models = self._get_models(iterable)
self.manager.update_docs(models)
# szy移除索引
def remove(self, obj_or_string):
models = self._get_models([obj_or_string])
self._delete(models)
# szy清空索引
def clear(self, models=None, commit=True):
self.remove(None)
# szy获取搜索建议词
@staticmethod
def get_suggestion(query: str) -> str:
"""获取推荐词, 如果没有找到添加原搜索词"""
@ -71,6 +79,7 @@ class ElasticSearchBackend(BaseSearchBackend):
return ' '.join(keywords)
# szy执行搜索并返回结果
@log_query
def search(self, query_string, **kwargs):
logger.info('search query_string:' + query_string)
@ -84,10 +93,13 @@ class ElasticSearchBackend(BaseSearchBackend):
else:
suggestion = query_string
# szy构建查询条件匹配标题或正文设置最小匹配度
q = Q('bool',
should=[Q('match', body=suggestion), Q('match', title=suggestion)],
minimum_should_match="70%")
# szy执行搜索查询过滤已发布的状态和文章类型
search = ArticleDocument.search() \
.query('bool', filter=[q]) \
.filter('term', status='p') \
@ -97,6 +109,8 @@ class ElasticSearchBackend(BaseSearchBackend):
results = search.execute()
hits = results['hits'].total
raw_results = []
# szy处理搜索结果构建SearchResult对象
for raw_result in results['hits']['hits']:
app_label = 'blog'
model_name = 'Article'
@ -112,6 +126,8 @@ class ElasticSearchBackend(BaseSearchBackend):
**additional_fields)
raw_results.append(result)
facets = {}
# szy设置拼写建议如果查询词与建议词不同则返回建议词
spelling_suggestion = None if query_string == suggestion else suggestion
return {
@ -121,7 +137,7 @@ class ElasticSearchBackend(BaseSearchBackend):
'spelling_suggestion': spelling_suggestion,
}
# szy定义Elasticsearch查询类
class ElasticSearchQuery(BaseSearchQuery):
def _convert_datetime(self, date):
if hasattr(date, 'hour'):
@ -129,6 +145,7 @@ class ElasticSearchQuery(BaseSearchQuery):
else:
return force_str(date.strftime('%Y%m%d000000'))
# szy清理查询片段处理保留字和特殊字符
def clean(self, query_fragment):
"""
Provides a mechanism for sanitizing user input before presenting the
@ -154,30 +171,35 @@ class ElasticSearchQuery(BaseSearchQuery):
return ' '.join(cleaned_words)
# szy构建查询片段
def build_query_fragment(self, field, filter_type, value):
return value.query_string
# szy获取查询结果数量
def get_count(self):
results = self.get_results()
return len(results) if results else 0
# szy获取拼写建议
def get_spelling_suggestion(self, preferred_query=None):
return self._spelling_suggestion
# szy构建查询参数
def build_params(self, spelling_query=None):
kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query)
return kwargs
# szy定义Elasticsearch模型搜索表单
class ElasticSearchModelSearchForm(ModelSearchForm):
# szy重写搜索方法支持建议搜索功能
def search(self):
# 是否建议搜索
self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no"
sqs = super().search()
return sqs
# szy定义Elasticsearch搜索引擎
class ElasticSearchEngine(BaseEngine):
backend = ElasticSearchBackend
query = ElasticSearchQuery

@ -6,35 +6,44 @@ from django.utils.feedgenerator import Rss201rev2Feed
from blog.models import Article
from djangoblog.utils import CommonMarkdown
# szy定义Django博客的RSS订阅类
class DjangoBlogFeed(Feed):
feed_type = Rss201rev2Feed
# szy设置Feed的描述、标题和链接
description = '大巧无工,重剑无锋.'
title = "且听风吟 大巧无工,重剑无锋. "
link = "/feed/"
# szy获取作者名称博客主昵称
def author_name(self):
return get_user_model().objects.first().nickname
# szy获取作者链接地址
def author_link(self):
return get_user_model().objects.first().get_absolute_url()
# szy获取要包含在Feed中的文章项
def items(self):
return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5]
# szy获取文章项的标题
def item_title(self, item):
return item.title
# szy获取文章项的描述使用Markdown渲染正文
def item_description(self, item):
return CommonMarkdown.get_markdown(item.body)
# szy设置Feed的版权信息
def feed_copyright(self):
now = timezone.now()
return "Copyright© {year} 且听风吟".format(year=now.year)
# szy获取文章项的链接地址
def item_link(self, item):
return item.get_absolute_url()
# szy获取文章项的全局唯一标识符
def item_guid(self, item):
return

@ -7,21 +7,26 @@ from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
# szy定义日志条目管理类
class LogEntryAdmin(admin.ModelAdmin):
# szy设置列表页过滤器字段
list_filter = [
'content_type'
]
# szy设置搜索字段
search_fields = [
'object_repr',
'change_message'
]
# szy设置列表页可点击链接的字段
list_display_links = [
'action_time',
'get_change_message',
]
# szy设置列表页显示的字段
list_display = [
'action_time',
'user_link',
@ -30,18 +35,22 @@ class LogEntryAdmin(admin.ModelAdmin):
'get_change_message',
]
# szy禁用添加权限
def has_add_permission(self, request):
return False
# szy设置修改权限仅超级用户或有特定权限的用户可以查看
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'
# szy禁用删除权限
def has_delete_permission(self, request, obj=None):
return False
# szy创建对象链接如果是删除操作则不生成链接
def object_link(self, obj):
object_link = escape(obj.object_repr)
content_type = obj.content_type
@ -62,6 +71,7 @@ class LogEntryAdmin(admin.ModelAdmin):
object_link.admin_order_field = 'object_repr'
object_link.short_description = _('object')
# szy创建用户链接链接到用户编辑页面
def user_link(self, obj):
content_type = ContentType.objects.get_for_model(type(obj.user))
user_link = escape(force_str(obj.user))
@ -80,10 +90,12 @@ class LogEntryAdmin(admin.ModelAdmin):
user_link.admin_order_field = 'user'
user_link.short_description = _('user')
# szy优化查询集预取关联的内容类型数据
def get_queryset(self, request):
queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type')
# szy重写获取actions的方法移除删除选中项的action
def get_actions(self, request):
actions = super(LogEntryAdmin, self).get_actions(request)
if 'delete_selected' in actions:

@ -2,19 +2,21 @@ import logging
logger = logging.getLogger(__name__)
# szy基础插件类所有插件都应该继承此类
class BasePlugin:
# 插件元数据
# szy插件元数据定义
PLUGIN_NAME = None
PLUGIN_DESCRIPTION = None
PLUGIN_VERSION = None
# szy插件初始化方法
def __init__(self):
if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]):
raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.")
self.init_plugin()
self.register_hooks()
# szy插件初始化逻辑子类可重写实现特定初始化
def init_plugin(self):
"""
插件初始化逻辑
@ -22,6 +24,7 @@ class BasePlugin:
"""
logger.info(f'{self.PLUGIN_NAME} initialized.')
# szy注册插件钩子子类可重写实现特定钩子注册
def register_hooks(self):
"""
注册插件钩子
@ -29,6 +32,7 @@ class BasePlugin:
"""
pass
# szy获取插件信息
def get_plugin_info(self):
"""
获取插件信息

@ -1,7 +1,10 @@
# szy文章详情加载事件钩子名称
ARTICLE_DETAIL_LOAD = 'article_detail_load'
# szy文章创建事件钩子名称
ARTICLE_CREATE = 'article_create'
# szy文章更新事件钩子名称
ARTICLE_UPDATE = 'article_update'
# szy文章删除事件钩子名称
ARTICLE_DELETE = 'article_delete'
ARTICLE_CONTENT_HOOK_NAME = "the_content"
# szy文章内容处理钩子名称
ARTICLE_CONTENT_HOOK_NAME = "the_content"

@ -1,7 +1,7 @@
import logging
logger = logging.getLogger(__name__)
# szy全局钩子字典存储所有注册的钩子
_hooks = {}
@ -34,11 +34,15 @@ def apply_filters(hook_name: str, value, *args, **kwargs):
执行一个 Filter Hook
它会把 value 依次传递给所有注册的回调函数进行处理
"""
# szy检查钩子是否存在存在则依次应用所有过滤器
if hook_name in _hooks:
logger.debug(f"Applying filter hook '{hook_name}'")
for callback in _hooks[hook_name]:
try:
# szy将当前值传递给回调函数进行处理并更新值
value = callback(value, *args, **kwargs)
except Exception as e:
# szy捕获并记录过滤器执行过程中的异常
logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True)
# szy返回处理后的最终值
return value

@ -9,11 +9,16 @@ 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.
"""
# szy遍历settings中配置的激活插件列表
for plugin_name in settings.ACTIVE_PLUGINS:
# szy构建插件路径
plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name)
# szy检查插件目录是否存在且包含plugin.py文件
if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')):
try:
# szy动态导入插件模块
__import__(f'plugins.{plugin_name}.plugin')
logger.info(f"Successfully loaded plugin: {plugin_name}")
except ImportError as e:
# szy记录插件导入失败的错误信息
logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e)

@ -15,12 +15,12 @@ from pathlib import Path
from django.utils.translation import gettext_lazy as _
# szy将环境变量转换为布尔值
def env_to_bool(env, default):
str_val = os.environ.get(env)
return default if str_val is None else str_val == 'True'
# szy构建项目基础路径
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']
CSRF_TRUSTED_ORIGINS = ['http://example.com']
# Application definition
# szy已安装的应用列表
INSTALLED_APPS = [
# 'django.contrib.admin',
'django.contrib.admin.apps.SimpleAdminConfig',
@ -64,6 +64,7 @@ INSTALLED_APPS = [
'djangoblog'
]
# szy中间件配置
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
@ -83,6 +84,7 @@ MIDDLEWARE = [
ROOT_URLCONF = 'djangoblog.urls'
# szy模板配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -106,6 +108,7 @@ WSGI_APPLICATION = 'djangoblog.wsgi.application'
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
# szyMySQL数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
@ -119,6 +122,7 @@ DATABASES = {
'charset': 'utf8mb4'},
}}
# szy密码验证配置
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
@ -137,23 +141,26 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
# szy国际化配置 - 支持的语言
LANGUAGES = (
('en', _('English')),
('zh-hans', _('Simplified Chinese')),
('zh-hant', _('Traditional Chinese')),
)
# szy本地化文件路径
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
# szy默认语言代码
LANGUAGE_CODE = 'zh-hans'
# szy时区设置
TIME_ZONE = 'Asia/Shanghai'
# szy启用国际化
USE_I18N = True
# szy启用本地化
USE_L10N = True
# szy使用时区支持
USE_TZ = False
# Static files (CSS, JavaScript, Images)
@ -180,6 +187,7 @@ STATICFILES = os.path.join(BASE_DIR, 'static')
AUTH_USER_MODEL = 'accounts.BlogUser'
LOGIN_URL = '/login/'
# szy时间和日期格式
TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
DATE_TIME_FORMAT = '%Y-%m-%d'
@ -188,11 +196,13 @@ BOOTSTRAP_COLOR_TYPES = [
'default', 'primary', 'success', 'info', 'warning', 'danger'
]
# szy分页设置
# paginate
PAGINATE_BY = 10
# http cache timeout
CACHE_CONTROL_MAX_AGE = 2592000
# cache setting
# szy缓存配置
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
@ -208,11 +218,12 @@ if os.environ.get("DJANGO_REDIS_URL"):
'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}',
}
}
# szy站点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'
# szy邮件配置
# Email:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False)
@ -229,6 +240,7 @@ ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')]
WXADMIN = os.environ.get(
'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7'
# szy日志配置
LOG_PATH = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOG_PATH):
os.makedirs(LOG_PATH, exist_ok=True)
@ -300,6 +312,7 @@ STATICFILES_FINDERS = (
# other
'compressor.finders.CompressorFinder',
)
# szy启用压缩
COMPRESS_ENABLED = True
# COMPRESS_OFFLINE = True
@ -314,6 +327,7 @@ COMPRESS_JS_FILTERS = [
'compressor.filters.jsmin.JSMinFilter'
]
# szy媒体文件配置
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'
X_FRAME_OPTIONS = 'SAMEORIGIN'
@ -332,6 +346,7 @@ if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
},
}
# szy插件系统配置
# Plugin System
PLUGINS_DIR = BASE_DIR / 'plugins'
ACTIVE_PLUGINS = [

@ -3,57 +3,72 @@ from django.urls import reverse
from blog.models import Article, Category, Tag
# szy静态视图站点地图类
class StaticViewSitemap(Sitemap):
priority = 0.5
changefreq = 'daily'
# szy定义包含在站点地图中的项
def items(self):
return ['blog:index', ]
# szy获取项的URL位置
def location(self, item):
return reverse(item)
# szy文章站点地图类
class ArticleSiteMap(Sitemap):
# szy设置文章更新频率和优先级
changefreq = "monthly"
priority = "0.6"
# szy获取所有已发布的文章
def items(self):
return Article.objects.filter(status='p')
# szy获取文章的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
# szy分类站点地图类
class CategorySiteMap(Sitemap):
# szy设置分类更新频率和优先级
changefreq = "Weekly"
priority = "0.6"
# szy获取所有分类
def items(self):
return Category.objects.all()
# szy获取分类的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
# szy标签站点地图类
class TagSiteMap(Sitemap):
# szy设置标签更新频率和优先级
changefreq = "Weekly"
priority = "0.3"
# szy获取所有标签
def items(self):
return Tag.objects.all()
# szy获取标签的最后修改时间
def lastmod(self, obj):
return obj.last_modify_time
# szy用户站点地图类
class UserSiteMap(Sitemap):
# szy设置用户更新频率和优先级
changefreq = "Weekly"
priority = "0.3"
# szy获取所有有文章的作者去重
def items(self):
return list(set(map(lambda x: x.author, Article.objects.all())))
# szy获取用户的注册时间
def lastmod(self, obj):
return obj.date_joined

@ -6,16 +6,21 @@ from django.conf import settings
logger = logging.getLogger(__name__)
# szy蜘蛛爬虫通知类用于向搜索引擎推送URL
class SpiderNotify():
@staticmethod
def baidu_notify(urls):
try:
# szy将URL列表转换为换行分隔的字符串
data = '\n'.join(urls)
result = requests.post(settings.BAIDU_NOTIFY_URL, data=data)
# szy记录推送结果日志
logger.info(result.text)
except Exception as e:
# szy记录推送异常日志
logger.error(e)
# szy推送单个URL的便捷方法
@staticmethod
def notify(url):
SpiderNotify.baidu_notify(url)

@ -3,13 +3,18 @@ from django.test import TestCase
from djangoblog.utils import *
# szyDjangoBlog测试类用于测试工具函数
class DjangoBlogTest(TestCase):
# szy测试初始化设置
def setUp(self):
pass
# szy测试工具函数功能
def test_utils(self):
# szy测试SHA256加密函数
md5 = get_sha256('test')
self.assertIsNotNone(md5)
# szy测试Markdown解析函数
c = CommonMarkdown.get_markdown('''
# Title1
@ -24,6 +29,8 @@ class DjangoBlogTest(TestCase):
''')
self.assertIsNotNone(c)
# szy测试字典转URL参数字符串函数
d = {
'd': 'key1',
'd2': 'key2'

@ -27,6 +27,7 @@ from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm
from djangoblog.feeds import DjangoBlogFeed
from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap
# szy定义站点地图配置
sitemaps = {
'blog': ArticleSiteMap,
@ -36,29 +37,45 @@ sitemaps = {
'static': StaticViewSitemap
}
# szy定义错误处理视图
handler404 = 'blog.views.page_not_found_view'
handler500 = 'blog.views.server_error_view'
handle403 = 'blog.views.permission_denied_view'
# szy基础URL模式配置
urlpatterns = [
path('i18n/', include('django.conf.urls.i18n')),
]
# szy国际化URL模式配置
urlpatterns += i18n_patterns(
# szy管理员后台URL
re_path(r'^admin/', admin_site.urls),
# szy博客应用URL
re_path(r'', include('blog.urls', namespace='blog')),
# szyMarkdown编辑器URL
re_path(r'mdeditor/', include('mdeditor.urls')),
# szy评论系统URL
re_path(r'', include('comments.urls', namespace='comment')),
# szy账户管理URL
re_path(r'', include('accounts.urls', namespace='account')),
# szyOAuth认证URL
re_path(r'', include('oauth.urls', namespace='oauth')),
# szy站点地图URL
re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
# szyFeed订阅URL
re_path(r'^feed/$', DjangoBlogFeed()),
# szyRSS订阅URL
re_path(r'^rss/$', DjangoBlogFeed()),
# szy搜索功能URL使用ElasticSearch视图和表单
re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm),
name='search'),
# szy服务器管理URL
re_path(r'', include('servermanager.urls', namespace='servermanager')),
# szy位置追踪URL
re_path(r'', include('owntracks.urls', namespace='owntracks'))
, prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
# szy调试模式下添加媒体文件服务
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)

@ -19,18 +19,18 @@ from django.templatetags.static import static
logger = logging.getLogger(__name__)
# szy获取最大文章ID和评论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)
# szy计算字符串的SHA256哈希值
def get_sha256(str):
m = sha256(str.encode('utf-8'))
return m.hexdigest()
# szy缓存装饰器用于函数结果缓存
def cache_decorator(expiration=3 * 60):
def wrapper(func):
def news(*args, **kwargs):
@ -67,6 +67,7 @@ def cache_decorator(expiration=3 * 60):
return wrapper
# szy刷新视图缓存
def expire_view_cache(path, servername, serverport, key_prefix=None):
'''
刷新视图缓存
@ -91,14 +92,15 @@ def expire_view_cache(path, servername, serverport, key_prefix=None):
return True
return False
# szy获取当前站点信息带缓存
@cache_decorator()
def get_current_site():
site = Site.objects.get_current()
return site
# szy通用Markdown处理类
class CommonMarkdown:
# szy转换Markdown文本为HTML
@staticmethod
def _convert_markdown(value):
md = markdown.Markdown(
@ -113,17 +115,21 @@ class CommonMarkdown:
toc = md.toc
return body, toc
# szy获取带目录的Markdown内容
@staticmethod
def get_markdown_with_toc(value):
body, toc = CommonMarkdown._convert_markdown(value)
return body, toc
# szy获取Markdown内容
@staticmethod
def get_markdown(value):
body, toc = CommonMarkdown._convert_markdown(value)
return body
# szy发送邮件函数
def send_email(emailto, title, content):
from djangoblog.blog_signals import send_email_signal
send_email_signal.send(
@ -132,12 +138,13 @@ def send_email(emailto, title, content):
title=title,
content=content)
# szy生成6位随机数字验证码
def generate_code() -> str:
"""生成随机数验证码"""
return ''.join(random.sample(string.digits, 6))
# szy将字典转换为URL参数字符串
def parse_dict_to_url(dict):
from urllib.parse import quote
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))
@ -145,6 +152,7 @@ def parse_dict_to_url(dict):
return url
# szy获取博客设置
def get_blog_setting():
value = cache.get('get_blog_setting')
if value:
@ -173,6 +181,7 @@ def get_blog_setting():
return value
# szy保存用户头像到本地
def save_user_avatar(url):
'''
保存用户头像
@ -201,6 +210,7 @@ def save_user_avatar(url):
return static('blog/img/avatar.png')
# szy删除侧边栏缓存
def delete_sidebar_cache():
from blog.models import LinkShowType
keys = ["sidebar" + x for x in LinkShowType.values]
@ -209,12 +219,14 @@ def delete_sidebar_cache():
cache.delete(k)
# szy删除视图缓存
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)
# szy获取资源URL
def get_resource_url():
if settings.STATIC_URL:
return settings.STATIC_URL
@ -227,6 +239,6 @@ ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'l
'h2', 'p']
ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
# szy清洗HTML内容移除不安全的标签和属性
def sanitize_html(html):
return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)

@ -40,6 +40,7 @@ except ImportError:
raise MissingDependency(
"The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.")
# szy处理最低版本要求
# Handle minimum requirement.
if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):
raise MissingDependency(
@ -47,12 +48,13 @@ if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):
# Bubble up the correct error.
# szy日期时间正则表达式用于解析日期字符串
DATETIME_REGEX = re.compile(
'^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(\.\d{3,6}Z?)?$')
LOCALS = threading.local()
LOCALS.RAM_STORE = None
# szy自定义Whoosh HTML格式化器用于保持跨后端一致的高亮结果
class WhooshHtmlFormatter(HtmlFormatter):
"""
This is a HtmlFormatter simpler than the whoosh.HtmlFormatter.
@ -62,6 +64,7 @@ class WhooshHtmlFormatter(HtmlFormatter):
template = '<%(tag)s>%(t)s</%(tag)s>'
# szyWhoosh搜索后端主类
class WhooshSearchBackend(BaseSearchBackend):
# Word reserved by Whoosh for special use.
RESERVED_WORDS = (
@ -79,6 +82,7 @@ class WhooshSearchBackend(BaseSearchBackend):
'[', ']', '^', '"', '~', '*', '?', ':', '.',
)
# szy初始化Whoosh后端
def __init__(self, connection_alias, **connection_options):
super(
WhooshSearchBackend,
@ -103,6 +107,7 @@ class WhooshSearchBackend(BaseSearchBackend):
self.log = logging.getLogger('haystack')
# szy设置Whoosh索引和配置
def setup(self):
"""
Defers loading until needed.
@ -110,6 +115,7 @@ class WhooshSearchBackend(BaseSearchBackend):
from haystack import connections
new_index = False
# szy确保索引目录存在如果不存在则创建
# Make sure the index is there.
if self.use_file_storage and not os.path.exists(self.path):
os.makedirs(self.path)
@ -120,6 +126,7 @@ class WhooshSearchBackend(BaseSearchBackend):
"The path to your Whoosh index '%s' is not writable for the current user/group." %
self.path)
# szy根据配置选择文件存储或内存存储
if self.use_file_storage:
self.storage = FileStorage(self.path)
else:
@ -134,6 +141,7 @@ class WhooshSearchBackend(BaseSearchBackend):
connections[self.connection_alias].get_unified_index().all_searchfields())
self.parser = QueryParser(self.content_field_name, schema=self.schema)
# szy创建或打开索引
if new_index is True:
self.index = self.storage.create_index(self.schema)
else:
@ -144,6 +152,7 @@ class WhooshSearchBackend(BaseSearchBackend):
self.setup_complete = True
# szy构建Whoosh schema定义字段类型
def build_schema(self, fields):
schema_fields = {
ID: WHOOSH_ID(stored=True, unique=True),
@ -199,6 +208,7 @@ class WhooshSearchBackend(BaseSearchBackend):
return (content_field_name, Schema(**schema_fields))
# szy更新索引
def update(self, index, iterable, commit=True):
if not self.setup_complete:
self.setup()
@ -206,6 +216,7 @@ class WhooshSearchBackend(BaseSearchBackend):
self.index = self.index.refresh()
writer = AsyncWriter(self.index)
# szy遍历对象并更新索引
for obj in iterable:
try:
doc = index.full_prepare(obj)
@ -244,6 +255,7 @@ class WhooshSearchBackend(BaseSearchBackend):
# otherwise.
writer.commit()
# szy从索引中移除对象
def remove(self, obj_or_string, commit=True):
if not self.setup_complete:
self.setup()
@ -266,6 +278,7 @@ class WhooshSearchBackend(BaseSearchBackend):
e,
exc_info=True)
# szy清空索引
def clear(self, models=None, commit=True):
if not self.setup_complete:
self.setup()
@ -303,6 +316,8 @@ class WhooshSearchBackend(BaseSearchBackend):
self.log.error(
"Failed to clear Whoosh index: %s", e, exc_info=True)
# szy删除整个索引
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.
@ -311,9 +326,11 @@ class WhooshSearchBackend(BaseSearchBackend):
elif not self.use_file_storage:
self.storage.clean()
# szy重新创建所有内容
# Recreate everything.
self.setup()
# szy优化索引
def optimize(self):
if not self.setup_complete:
self.setup()
@ -321,12 +338,14 @@ class WhooshSearchBackend(BaseSearchBackend):
self.index = self.index.refresh()
self.index.optimize()
# szy计算分页信息
def calculate_page(self, start_offset=0, end_offset=None):
# Prevent against Whoosh throwing an error. Requires an end_offset
# greater than 0.
if end_offset is not None and end_offset <= 0:
end_offset = 1
# szy确定页码
# Determine the page.
page_num = 0
@ -345,6 +364,8 @@ class WhooshSearchBackend(BaseSearchBackend):
page_num += 1
return page_num, page_length
# szy执行搜索查询
@log_query
def search(
self,
@ -388,6 +409,8 @@ class WhooshSearchBackend(BaseSearchBackend):
reverse = False
# szy处理排序
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
@ -560,6 +583,8 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': spelling_suggestion,
}
# szy实现"更多类似此结果"功能
def more_like_this(
self,
model_instance,
@ -675,6 +700,8 @@ class WhooshSearchBackend(BaseSearchBackend):
return results
# szy处理原始搜索结果转换为Haystack格式
def _process_results(
self,
raw_page,
@ -767,6 +794,8 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': spelling_suggestion,
}
# szy创建拼写建议
def create_spelling_suggestion(self, query_string):
spelling_suggestion = None
reader = self.index.reader()
@ -819,6 +848,8 @@ class WhooshSearchBackend(BaseSearchBackend):
value = force_str(value)
return value
# szy将Whoosh值转换为Python原生值
def _to_python(self, value):
"""
Converts values from Whoosh to native Python values.
@ -870,6 +901,7 @@ class WhooshSearchBackend(BaseSearchBackend):
return value
# szyWhoosh搜索查询类
class WhooshSearchQuery(BaseSearchQuery):
def _convert_datetime(self, date):
if hasattr(date, 'hour'):
@ -877,6 +909,8 @@ class WhooshSearchQuery(BaseSearchQuery):
else:
return force_str(date.strftime('%Y%m%d000000'))
# szy清理查询片段处理保留字和特殊字符
def clean(self, query_fragment):
"""
Provides a mechanism for sanitizing user input before presenting the
@ -902,6 +936,8 @@ class WhooshSearchQuery(BaseSearchQuery):
return ' '.join(cleaned_words)
# szy构建查询片段
def build_query_fragment(self, field, filter_type, value):
from haystack import connections
query_frag = ''
@ -1039,6 +1075,7 @@ class WhooshSearchQuery(BaseSearchQuery):
# value = self.backend._from_python(value)
# szyWhoosh搜索引擎类
class WhooshEngine(BaseEngine):
backend = WhooshSearchBackend
query = WhooshSearchQuery

@ -11,6 +11,8 @@ import os
from django.core.wsgi import get_wsgi_application
# szy设置Django的默认设置模块环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
# szy获取WSGI应用实例用于服务器部署
application = get_wsgi_application()

Loading…
Cancel
Save