Initial commit: djangoblog core

ZXY 4 months ago
commit fba1ac0eff

@ -0,0 +1,2 @@
# Zxy指定默认的应用配置类
default_app_config = 'djangoblog.apps.DjangoblogAppConfig'

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,60 @@
# 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)

@ -0,0 +1,16 @@
# 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加载插件

@ -0,0 +1,133 @@
# 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清理所有缓存暂时注释

@ -0,0 +1,196 @@
# 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查询类

@ -0,0 +1,54 @@
# 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

@ -0,0 +1,88 @@
# 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 = '<a href="{}">{}</a>'.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 = '<a href="{}">{}</a>'.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

@ -0,0 +1,48 @@
# 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插件版本
}

@ -0,0 +1,8 @@
# 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文章内容处理的钩子名称

@ -0,0 +1,47 @@
# 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返回处理后的值

@ -0,0 +1,24 @@
# 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记录加载失败日志

@ -0,0 +1,344 @@
"""
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'
]

@ -0,0 +1,70 @@
# 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

@ -0,0 +1,26 @@
# 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)

@ -0,0 +1,38 @@
# 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断言返回值不为空

@ -0,0 +1,68 @@
# 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)

@ -0,0 +1,198 @@
#!/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)

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,20 @@
"""
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()
Loading…
Cancel
Save