master
ZXY 1 month ago
parent 5b0854e397
commit 2e62fc2db4

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

@ -1,8 +1,10 @@
# Zxy导入 Django 的 AdminSite 类和其他相关模块
from django.contrib.admin import AdminSite from django.contrib.admin import AdminSite
from django.contrib.admin.models import LogEntry from django.contrib.admin.models import LogEntry
from django.contrib.sites.admin import SiteAdmin from django.contrib.sites.admin import SiteAdmin
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
# Zxy导入自定义模块和模型
from accounts.admin import * from accounts.admin import *
from blog.admin import * from blog.admin import *
from blog.models import * from blog.models import *
@ -16,31 +18,25 @@ from owntracks.models import *
from servermanager.admin import * from servermanager.admin import *
from servermanager.models import * from servermanager.models import *
# Zxy定义自定义 AdminSite 类
class DjangoBlogAdminSite(AdminSite): class DjangoBlogAdminSite(AdminSite):
# 自定义站点标题和头部
site_header = 'djangoblog administration' site_header = 'djangoblog administration'
site_title = 'djangoblog site admin' site_title = 'djangoblog site admin'
# 初始化方法
def __init__(self, name='admin'): def __init__(self, name='admin'):
super().__init__(name) super().__init__(name)
# 定义权限检查方法,仅允许超级用户访问
def has_permission(self, request): def has_permission(self, request):
return request.user.is_superuser return request.user.is_superuser
# def get_urls(self): # Zxy创建自定义 AdminSite 实例
# urls = super().get_urls()
# from django.urls import path
# from blog.views import refresh_memcache
#
# my_urls = [
# path('refresh/', self.admin_view(refresh_memcache), name="refresh"),
# ]
# return urls + my_urls
admin_site = DjangoBlogAdminSite(name='admin') admin_site = DjangoBlogAdminSite(name='admin')
admin_site.register(Article, ArticlelAdmin) # Zxy注册模型到自定义 AdminSite
admin_site.register(Article, ArticleAdmin)
admin_site.register(Category, CategoryAdmin) admin_site.register(Category, CategoryAdmin)
admin_site.register(Tag, TagAdmin) admin_site.register(Tag, TagAdmin)
admin_site.register(Links, LinksAdmin) admin_site.register(Links, LinksAdmin)
@ -61,4 +57,4 @@ admin_site.register(OwnTrackLog, OwnTrackLogsAdmin)
admin_site.register(Site, SiteAdmin) admin_site.register(Site, SiteAdmin)
admin_site.register(LogEntry, LogEntryAdmin) admin_site.register(LogEntry, LogEntryAdmin)

@ -1,11 +1,16 @@
# Zxy导入Django的AppConfig模块
from django.apps import AppConfig from django.apps import AppConfig
# Zxy定义Djangoblog应用的配置类
class DjangoblogAppConfig(AppConfig): class DjangoblogAppConfig(AppConfig):
# Zxy设置默认的自动字段类型
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
# Zxy应用名称
name = 'djangoblog' name = 'djangoblog'
# Zxy重写ready方法用于初始化应用
def ready(self): def ready(self):
super().ready() super().ready() # Zxy调用父类的ready方法
# Import and load plugins here # Zxy导入并加载插件
from .plugin_manage.loader import load_plugins from .plugin_manage.loader import load_plugins
load_plugins() load_plugins() # Zxy加载插件

@ -1,122 +1,133 @@
# Zxy导入线程模块
import _thread import _thread
# Zxy导入日志模块
import logging import logging
# Zxy导入Django的信号模块
import django.dispatch import django.dispatch
# Zxy导入Django的配置模块
from django.conf import settings from django.conf import settings
# Zxy导入Django的LogEntry模型
from django.contrib.admin.models import LogEntry from django.contrib.admin.models import LogEntry
# Zxy导入用户登录和登出信号
from django.contrib.auth.signals import user_logged_in, user_logged_out from django.contrib.auth.signals import user_logged_in, user_logged_out
# Zxy导入邮件发送模块
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
# Zxy导入模型保存信号
from django.db.models.signals import post_save from django.db.models.signals import post_save
# Zxy导入信号接收器
from django.dispatch import receiver from django.dispatch import receiver
# Zxy导入评论模型
from comments.models import Comment from comments.models import Comment
# Zxy导入发送评论邮件的工具函数
from comments.utils import send_comment_email from comments.utils import send_comment_email
# Zxy导入爬虫通知工具
from djangoblog.spider_notify import SpiderNotify from djangoblog.spider_notify import SpiderNotify
# Zxy导入缓存和缓存清理工具
from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache
# Zxy导入获取当前站点的工具函数
from djangoblog.utils import get_current_site from djangoblog.utils import get_current_site
# Zxy导入OAuth用户模型
from oauth.models import OAuthUser from oauth.models import OAuthUser
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy定义OAuth用户登录信号
oauth_user_login_signal = django.dispatch.Signal(['id']) oauth_user_login_signal = django.dispatch.Signal(['id'])
send_email_signal = django.dispatch.Signal( # Zxy定义发送邮件信号
['emailto', 'title', 'content']) send_email_signal = django.dispatch.Signal(['emailto', 'title', 'content'])
# Zxy定义发送邮件信号的处理函数
@receiver(send_email_signal) @receiver(send_email_signal)
def send_email_signal_handler(sender, **kwargs): def send_email_signal_handler(sender, **kwargs):
emailto = kwargs['emailto'] emailto = kwargs['emailto'] # Zxy收件人
title = kwargs['title'] title = kwargs['title'] # Zxy邮件标题
content = kwargs['content'] content = kwargs['content'] # Zxy邮件内容
msg = EmailMultiAlternatives( msg = EmailMultiAlternatives( # Zxy创建邮件对象
title, title,
content, content,
from_email=settings.DEFAULT_FROM_EMAIL, from_email=settings.DEFAULT_FROM_EMAIL, # Zxy发件人
to=emailto) to=emailto # Zxy收件人
msg.content_subtype = "html" )
msg.content_subtype = "html" # Zxy设置邮件内容类型为HTML
from servermanager.models import EmailSendLog from servermanager.models import EmailSendLog # Zxy导入邮件发送日志模型
log = EmailSendLog() log = EmailSendLog() # Zxy创建日志记录
log.title = title log.title = title
log.content = content log.content = content
log.emailto = ','.join(emailto) log.emailto = ','.join(emailto) # Zxy记录收件人
try: try:
result = msg.send() result = msg.send() # Zxy发送邮件
log.send_result = result > 0 log.send_result = result > 0 # Zxy记录发送结果
except Exception as e: except Exception as e:
logger.error(f"失败邮箱号: {emailto}, {e}") logger.error(f"失败邮箱号: {emailto}, {e}") # Zxy记录错误信息
log.send_result = False log.send_result = False
log.save() log.save() # Zxy保存日志
# Zxy定义OAuth用户登录信号的处理函数
@receiver(oauth_user_login_signal) @receiver(oauth_user_login_signal)
def oauth_user_login_signal_handler(sender, **kwargs): def oauth_user_login_signal_handler(sender, **kwargs):
id = kwargs['id'] id = kwargs['id'] # Zxy获取用户ID
oauthuser = OAuthUser.objects.get(id=id) oauthuser = OAuthUser.objects.get(id=id) # Zxy获取OAuth用户
site = get_current_site().domain site = get_current_site().domain # Zxy获取当前站点域名
if oauthuser.picture and not oauthuser.picture.find(site) >= 0: if oauthuser.picture and not oauthuser.picture.find(site) >= 0: # Zxy检查头像URL是否包含当前站点域名
from djangoblog.utils import save_user_avatar from djangoblog.utils import save_user_avatar # Zxy导入保存头像的工具函数
oauthuser.picture = save_user_avatar(oauthuser.picture) oauthuser.picture = save_user_avatar(oauthuser.picture) # Zxy保存用户头像
oauthuser.save() oauthuser.save() # Zxy保存用户信息
delete_sidebar_cache()
delete_sidebar_cache() # Zxy删除侧边栏缓存
# Zxy定义模型保存后的回调函数
@receiver(post_save) @receiver(post_save)
def model_post_save_callback( def model_post_save_callback(sender, instance, created, raw, using, update_fields, **kwargs):
sender, clearcache = False # Zxy标记是否需要清理缓存
instance, if isinstance(instance, LogEntry): # Zxy如果是LogEntry模型直接返回
created,
raw,
using,
update_fields,
**kwargs):
clearcache = False
if isinstance(instance, LogEntry):
return return
if 'get_full_url' in dir(instance): if 'get_full_url' in dir(instance): # Zxy检查是否有获取完整URL的方法
is_update_views = update_fields == {'views'} is_update_views = update_fields == {'views'} # Zxy检查是否是更新浏览次数
if not settings.TESTING and not is_update_views: if not settings.TESTING and not is_update_views: # Zxy如果不是测试环境且不是更新浏览次数
try: try:
notify_url = instance.get_full_url() notify_url = instance.get_full_url() # Zxy获取完整URL
SpiderNotify.baidu_notify([notify_url]) SpiderNotify.baidu_notify([notify_url]) # Zxy通知百度爬虫
except Exception as ex: except Exception as ex:
logger.error("notify sipder", ex) logger.error("notify spider", ex) # Zxy记录错误信息
if not is_update_views: if not is_update_views:
clearcache = True clearcache = True # Zxy标记清理缓存
if isinstance(instance, Comment): if isinstance(instance, Comment): # Zxy如果是评论模型
if instance.is_enable: if instance.is_enable: # Zxy检查评论是否启用
path = instance.article.get_absolute_url() path = instance.article.get_absolute_url() # Zxy获取文章的绝对URL
site = get_current_site().domain site = get_current_site().domain # Zxy获取当前站点域名
if site.find(':') > 0: if site.find(':') > 0: # Zxy去除端口号
site = site[0:site.find(':')] site = site[0:site.find(':')]
expire_view_cache( expire_view_cache( # Zxy清理视图缓存
path, path,
servername=site, servername=site,
serverport=80, serverport=80,
key_prefix='blogdetail') key_prefix='blogdetail'
if cache.get('seo_processor'): )
if cache.get('seo_processor'): # Zxy清理SEO处理器缓存
cache.delete('seo_processor') cache.delete('seo_processor')
comment_cache_key = 'article_comments_{id}'.format( comment_cache_key = 'article_comments_{id}'.format(id=instance.article.id) # Zxy生成评论缓存键
id=instance.article.id) cache.delete(comment_cache_key) # Zxy清理评论缓存
cache.delete(comment_cache_key) delete_sidebar_cache() # Zxy清理侧边栏缓存
delete_sidebar_cache() delete_view_cache('article_comments', [str(instance.article.pk)]) # Zxy清理文章评论缓存
delete_view_cache('article_comments', [str(instance.article.pk)])
_thread.start_new_thread(send_comment_email, (instance,)) _thread.start_new_thread(send_comment_email, (instance,)) # Zxy异步发送评论邮件
if clearcache: if clearcache: # Zxy清理缓存
cache.clear() cache.clear()
# Zxy定义用户登录和登出的回调函数
@receiver(user_logged_in) @receiver(user_logged_in)
@receiver(user_logged_out) @receiver(user_logged_out)
def user_auth_callback(sender, request, user, **kwargs): def user_auth_callback(sender, request, user, **kwargs):
if user and user.username: if user and user.username: # Zxy检查用户是否登录
logger.info(user) logger.info(user) # Zxy记录用户信息
delete_sidebar_cache() delete_sidebar_cache() # Zxy清理侧边栏缓存
# cache.clear() # cache.clear() # Zxy清理所有缓存暂时注释

@ -1,62 +1,74 @@
# Zxy导入Django的编码工具
from django.utils.encoding import force_str from django.utils.encoding import force_str
# Zxy导入Elasticsearch DSL的查询构造器
from elasticsearch_dsl import Q from elasticsearch_dsl import Q
# Zxy导入Haystack的搜索后端模块
from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query
# Zxy导入Haystack的表单模块
from haystack.forms import ModelSearchForm from haystack.forms import ModelSearchForm
# Zxy导入Haystack的搜索结果模型
from haystack.models import SearchResult from haystack.models import SearchResult
# Zxy导入Haystack的日志工具
from haystack.utils import log as logging from haystack.utils import log as logging
# Zxy导入项目中的Elasticsearch文档和管理器
from blog.documents import ArticleDocument, ArticleDocumentManager from blog.documents import ArticleDocument, ArticleDocumentManager
# Zxy导入项目中的文章模型
from blog.models import Article from blog.models import Article
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy定义Elasticsearch搜索后端类
class ElasticSearchBackend(BaseSearchBackend): class ElasticSearchBackend(BaseSearchBackend):
# Zxy初始化方法
def __init__(self, connection_alias, **connection_options): def __init__(self, connection_alias, **connection_options):
super( super(ElasticSearchBackend, self).__init__(connection_alias, **connection_options)
ElasticSearchBackend, self.manager = ArticleDocumentManager() # Zxy初始化文档管理器
self).__init__( self.include_spelling = True # Zxy启用拼写建议
connection_alias,
**connection_options)
self.manager = ArticleDocumentManager()
self.include_spelling = True
# Zxy获取模型数据并转换为文档
def _get_models(self, iterable): def _get_models(self, iterable):
models = iterable if iterable and iterable[0] else Article.objects.all() models = iterable if iterable and iterable[0] else Article.objects.all() # Zxy获取模型数据
docs = self.manager.convert_to_doc(models) docs = self.manager.convert_to_doc(models) # Zxy转换为Elasticsearch文档
return docs return docs
# Zxy创建索引并重建数据
def _create(self, models): def _create(self, models):
self.manager.create_index() self.manager.create_index() # Zxy创建索引
docs = self._get_models(models) docs = self._get_models(models) # Zxy获取文档
self.manager.rebuild(docs) self.manager.rebuild(docs) # Zxy重建索引数据
# Zxy删除模型数据
def _delete(self, models): def _delete(self, models):
for m in models: for m in models:
m.delete() m.delete() # Zxy删除模型实例
return True return True
# Zxy重建索引数据
def _rebuild(self, models): def _rebuild(self, models):
models = models if models else Article.objects.all() models = models if models else Article.objects.all() # Zxy获取模型数据
docs = self.manager.convert_to_doc(models) docs = self.manager.convert_to_doc(models) # Zxy转换为文档
self.manager.update_docs(docs) self.manager.update_docs(docs) # Zxy更新索引数据
# Zxy更新索引
def update(self, index, iterable, commit=True): def update(self, index, iterable, commit=True):
models = self._get_models(iterable) # Zxy获取文档
self.manager.update_docs(models) # Zxy更新索引
models = self._get_models(iterable) # Zxy删除文档
self.manager.update_docs(models)
def remove(self, obj_or_string): def remove(self, obj_or_string):
models = self._get_models([obj_or_string]) models = self._get_models([obj_or_string]) # Zxy获取文档
self._delete(models) self._delete(models) # Zxy删除文档
# Zxy清空索引
def clear(self, models=None, commit=True): def clear(self, models=None, commit=True):
self.remove(None) self.remove(None) # Zxy删除所有文档
# Zxy获取拼写建议
@staticmethod @staticmethod
def get_suggestion(query: str) -> str: def get_suggestion(query: str) -> str:
"""获取推荐词, 如果没有找到添加原搜索词""" """获取推荐词,如果没有找到则返回原搜索词"""
search = ArticleDocument.search() \ search = ArticleDocument.search() \
.query("match", body=query) \ .query("match", body=query) \
.suggest('suggest_search', query, term={'field': 'body'}) \ .suggest('suggest_search', query, term={'field': 'body'}) \
@ -64,80 +76,78 @@ class ElasticSearchBackend(BaseSearchBackend):
keywords = [] keywords = []
for suggest in search.suggest.suggest_search: for suggest in search.suggest.suggest_search:
if suggest["options"]: if suggest["options"]: # Zxy检查是否有建议选项
keywords.append(suggest["options"][0]["text"]) keywords.append(suggest["options"][0]["text"]) # Zxy添加建议词
else: else:
keywords.append(suggest["text"]) keywords.append(suggest["text"]) # Zxy添加原搜索词
return ' '.join(keywords) return ' '.join(keywords) # Zxy返回拼写建议
# Zxy执行搜索查询
@log_query @log_query
def search(self, query_string, **kwargs): def search(self, query_string, **kwargs):
logger.info('search query_string:' + query_string) logger.info('search query_string:' + query_string) # Zxy记录搜索查询
start_offset = kwargs.get('start_offset') start_offset = kwargs.get('start_offset') # Zxy获取起始偏移量
end_offset = kwargs.get('end_offset') end_offset = kwargs.get('end_offset') # Zxy获取结束偏移量
# 推荐词搜索 # Zxy检查是否启用拼写建议
if getattr(self, "is_suggest", None): if getattr(self, "is_suggest", None):
suggestion = self.get_suggestion(query_string) suggestion = self.get_suggestion(query_string) # Zxy获取拼写建议
else: else:
suggestion = query_string suggestion = query_string # Zxy使用原搜索词
q = Q('bool', q = Q('bool', # Zxy构造布尔查询
should=[Q('match', body=suggestion), Q('match', title=suggestion)], should=[Q('match', body=suggestion), Q('match', title=suggestion)], # Zxy匹配标题或正文
minimum_should_match="70%") minimum_should_match="70%") # Zxy至少匹配70%
search = ArticleDocument.search() \ search = ArticleDocument.search() \
.query('bool', filter=[q]) \ .query('bool', filter=[q]) \
.filter('term', status='p') \ .filter('term', status='p') \
.filter('term', type='a') \ .filter('term', type='a') \
.source(False)[start_offset: end_offset] .source(False)[start_offset: end_offset] # Zxy执行搜索
results = search.execute() results = search.execute() # Zxy执行搜索查询
hits = results['hits'].total hits = results['hits'].total # Zxy获取总匹配数
raw_results = [] raw_results = [] # Zxy初始化结果列表
for raw_result in results['hits']['hits']:
app_label = 'blog'
model_name = 'Article'
additional_fields = {}
result_class = SearchResult for raw_result in results['hits']['hits']: # Zxy遍历搜索结果
app_label = 'blog' # Zxy应用标签
model_name = 'Article' # Zxy模型名称
additional_fields = {} # Zxy额外字段
result = result_class( result_class = SearchResult # Zxy搜索结果类
result = result_class( # Zxy创建搜索结果对象
app_label, app_label,
model_name, model_name,
raw_result['_id'], raw_result['_id'],
raw_result['_score'], raw_result['_score'],
**additional_fields) **additional_fields
raw_results.append(result) )
facets = {} raw_results.append(result) # Zxy添加到结果列表
spelling_suggestion = None if query_string == suggestion else suggestion
facets = {} # Zxy初始化分面信息
spelling_suggestion = None if query_string == suggestion else suggestion # Zxy拼写建议
return { return {
'results': raw_results, 'results': raw_results, # Zxy返回搜索结果
'hits': hits, 'hits': hits, # Zxy返回匹配数
'facets': facets, 'facets': facets, # Zxy返回分面信息
'spelling_suggestion': spelling_suggestion, 'spelling_suggestion': spelling_suggestion, # Zxy返回拼写建议
} }
# Zxy定义Elasticsearch搜索查询类
class ElasticSearchQuery(BaseSearchQuery): class ElasticSearchQuery(BaseSearchQuery):
# Zxy转换日期时间格式
def _convert_datetime(self, date): def _convert_datetime(self, date):
if hasattr(date, 'hour'): if hasattr(date, 'hour'):
return force_str(date.strftime('%Y%m%d%H%M%S')) return force_str(date.strftime('%Y%m%d%H%M%S'))
else: else:
return force_str(date.strftime('%Y%m%d000000')) return force_str(date.strftime('%Y%m%d000000'))
# Zxy清理用户输入
def clean(self, query_fragment): def clean(self, query_fragment):
"""
Provides a mechanism for sanitizing user input before presenting the
value to the backend.
Whoosh 1.X differs here in that you can no longer use a backslash
to escape reserved characters. Instead, the whole word should be
quoted.
"""
words = query_fragment.split() words = query_fragment.split()
cleaned_words = [] cleaned_words = []
@ -154,30 +164,33 @@ class ElasticSearchQuery(BaseSearchQuery):
return ' '.join(cleaned_words) return ' '.join(cleaned_words)
# Zxy构建查询片段
def build_query_fragment(self, field, filter_type, value): def build_query_fragment(self, field, filter_type, value):
return value.query_string return value.query_string
# Zxy获取结果数量
def get_count(self): def get_count(self):
results = self.get_results() results = self.get_results()
return len(results) if results else 0 return len(results) if results else 0
# Zxy获取拼写建议
def get_spelling_suggestion(self, preferred_query=None): def get_spelling_suggestion(self, preferred_query=None):
return self._spelling_suggestion return self._spelling_suggestion
# Zxy构建查询参数
def build_params(self, spelling_query=None): def build_params(self, spelling_query=None):
kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query) kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query)
return kwargs return kwargs
# Zxy定义Elasticsearch模型搜索表单
class ElasticSearchModelSearchForm(ModelSearchForm): class ElasticSearchModelSearchForm(ModelSearchForm):
# Zxy执行搜索
def search(self): def search(self):
# 是否建议搜索 self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no" # Zxy检查是否启用拼写建议
self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no" sqs = super().search() # Zxy调用父类搜索方法
sqs = super().search()
return sqs return sqs
# Zxy定义Elasticsearch搜索引擎
class ElasticSearchEngine(BaseEngine): class ElasticSearchEngine(BaseEngine):
backend = ElasticSearchBackend backend = ElasticSearchBackend # Zxy后端类
query = ElasticSearchQuery query = ElasticSearchQuery # Zxy查询类

@ -1,40 +1,54 @@
# Zxy导入Django的用户模型
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
# Zxy导入Django的Feed视图
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
# Zxy导入Django的时区工具
from django.utils import timezone from django.utils import timezone
# Zxy导入RSS 2.0 Feed生成器
from django.utils.feedgenerator import Rss201rev2Feed from django.utils.feedgenerator import Rss201rev2Feed
# Zxy导入项目中的文章模型
from blog.models import Article from blog.models import Article
# Zxy导入Markdown工具
from djangoblog.utils import CommonMarkdown from djangoblog.utils import CommonMarkdown
# Zxy定义Django博客Feed
class DjangoBlogFeed(Feed): class DjangoBlogFeed(Feed):
feed_type = Rss201rev2Feed feed_type = Rss201rev2Feed # Zxy使用RSS 2.0格式
description = '大巧无工,重剑无锋.' description = '大巧无工,重剑无锋.' # ZxyFeed描述
title = "且听风吟 大巧无工,重剑无锋. " title = "且听风吟 大巧无工,重剑无锋." # ZxyFeed标题
link = "/feed/" link = "/feed/" # ZxyFeed链接
# Zxy获取作者名称
def author_name(self): def author_name(self):
return get_user_model().objects.first().nickname return get_user_model().objects.first().nickname
# Zxy获取作者链接
def author_link(self): def author_link(self):
return get_user_model().objects.first().get_absolute_url() return get_user_model().objects.first().get_absolute_url()
# Zxy获取Feed项
def items(self): def items(self):
return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5] return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5] # Zxy获取最近5篇已发布的文章
# Zxy获取Feed项标题
def item_title(self, item): def item_title(self, item):
return item.title return item.title
# Zxy获取Feed项描述
def item_description(self, item): def item_description(self, item):
return CommonMarkdown.get_markdown(item.body) return CommonMarkdown.get_markdown(item.body) # Zxy将文章内容转换为Markdown格式
# Zxy获取Feed版权信息
def feed_copyright(self): def feed_copyright(self):
now = timezone.now() now = timezone.now()
return "Copyright© {year} 且听风吟".format(year=now.year) return "Copyright© {year} 且听风吟".format(year=now.year) # Zxy动态生成版权年份
# Zxy获取Feed项链接
def item_link(self, item): def item_link(self, item):
return item.get_absolute_url() return item.get_absolute_url()
# Zxy获取Feed项GUID
def item_guid(self, item): def item_guid(self, item):
return return

@ -1,91 +1,88 @@
# Zxy导入Django的admin模块
from django.contrib import admin from django.contrib import admin
# Zxy导入Django的LogEntry模型
from django.contrib.admin.models import DELETION from django.contrib.admin.models import DELETION
# Zxy导入Django的内容类型模型
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
# Zxy导入Django的URL工具
from django.urls import reverse, NoReverseMatch from django.urls import reverse, NoReverseMatch
# Zxy导入Django的编码工具
from django.utils.encoding import force_str from django.utils.encoding import force_str
# Zxy导入Django的HTML工具
from django.utils.html import escape from django.utils.html import escape
# Zxy导入Django的安全字符串工具
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
# Zxy导入Django的翻译工具
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Zxy定义LogEntryAdmin类
class LogEntryAdmin(admin.ModelAdmin): class LogEntryAdmin(admin.ModelAdmin):
list_filter = [ list_filter = ['content_type'] # Zxy按内容类型过滤
'content_type' search_fields = ['object_repr', 'change_message'] # Zxy搜索字段
]
search_fields = [
'object_repr',
'change_message'
]
list_display_links = [ list_display_links = ['action_time', 'get_change_message'] # Zxy显示链接的字段
'action_time', list_display = ['action_time', 'user_link', 'content_type', 'object_link', 'get_change_message'] # Zxy显示字段
'get_change_message',
]
list_display = [
'action_time',
'user_link',
'content_type',
'object_link',
'get_change_message',
]
# Zxy检查是否有添加权限
def has_add_permission(self, request): def has_add_permission(self, request):
return False return False
# Zxy检查是否有修改权限
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
return ( return (
request.user.is_superuser or request.user.is_superuser or
request.user.has_perm('admin.change_logentry') request.user.has_perm('admin.change_logentry')
) and request.method != 'POST' ) and request.method != 'POST'
# Zxy检查是否有删除权限
def has_delete_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None):
return False return False
# Zxy获取对象链接
def object_link(self, obj): def object_link(self, obj):
object_link = escape(obj.object_repr) object_link = escape(obj.object_repr) # Zxy转义对象表示
content_type = obj.content_type content_type = obj.content_type
if obj.action_flag != DELETION and content_type is not None: if obj.action_flag != DELETION and content_type is not None: # Zxy检查是否为删除操作
# try returning an actual link instead of object repr string
try: try:
url = reverse( url = reverse( # Zxy生成反向URL
'admin:{}_{}_change'.format(content_type.app_label, 'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
content_type.model),
args=[obj.object_id] args=[obj.object_id]
) )
object_link = '<a href="{}">{}</a>'.format(url, object_link) object_link = '<a href="{}">{}</a>'.format(url, object_link) # Zxy生成链接
except NoReverseMatch: except NoReverseMatch:
pass pass
return mark_safe(object_link) return mark_safe(object_link) # Zxy标记为安全字符串
object_link.admin_order_field = 'object_repr' object_link.admin_order_field = 'object_repr' # Zxy排序字段
object_link.short_description = _('object') object_link.short_description = _('object') # Zxy字段描述
# Zxy获取用户链接
def user_link(self, obj): def user_link(self, obj):
content_type = ContentType.objects.get_for_model(type(obj.user)) content_type = ContentType.objects.get_for_model(type(obj.user)) # Zxy获取用户的内容类型
user_link = escape(force_str(obj.user)) user_link = escape(force_str(obj.user)) # Zxy转义用户表示
try: try:
# try returning an actual link instead of object repr string url = reverse( # Zxy生成反向URL
url = reverse( 'admin:{}_{}_change'.format(content_type.app_label, content_type.model),
'admin:{}_{}_change'.format(content_type.app_label,
content_type.model),
args=[obj.user.pk] args=[obj.user.pk]
) )
user_link = '<a href="{}">{}</a>'.format(url, user_link) user_link = '<a href="{}">{}</a>'.format(url, user_link) # Zxy生成链接
except NoReverseMatch: except NoReverseMatch:
pass pass
return mark_safe(user_link) return mark_safe(user_link) # Zxy标记为安全字符串
user_link.admin_order_field = 'user' user_link.admin_order_field = 'user' # Zxy排序字段
user_link.short_description = _('user') user_link.short_description = _('user') # Zxy字段描述
# Zxy获取查询集
def get_queryset(self, request): def get_queryset(self, request):
queryset = super(LogEntryAdmin, self).get_queryset(request) queryset = super(LogEntryAdmin, self).get_queryset(request)
return queryset.prefetch_related('content_type') return queryset.prefetch_related('content_type') # Zxy预加载内容类型
# Zxy获取操作
def get_actions(self, request): def get_actions(self, request):
actions = super(LogEntryAdmin, self).get_actions(request) actions = super(LogEntryAdmin, self).get_actions(request)
if 'delete_selected' in actions: if 'delete_selected' in actions: # Zxy移除删除操作
del actions['delete_selected'] del actions['delete_selected']
return actions return actions

@ -1,27 +1,33 @@
# Zxy导入日志模块
import logging import logging
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy定义基础插件类
class BasePlugin: class BasePlugin:
# 插件元数据 # Zxy插件元数据
PLUGIN_NAME = None PLUGIN_NAME = None # Zxy插件名称
PLUGIN_DESCRIPTION = None PLUGIN_DESCRIPTION = None # Zxy插件描述
PLUGIN_VERSION = None PLUGIN_VERSION = None # Zxy插件版本
# Zxy初始化插件
def __init__(self): def __init__(self):
# Zxy检查插件元数据是否已定义
if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]): 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.") raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.")
self.init_plugin() self.init_plugin() # Zxy调用初始化方法
self.register_hooks() self.register_hooks() # Zxy注册钩子
# Zxy插件初始化逻辑
def init_plugin(self): def init_plugin(self):
""" """
插件初始化逻辑 插件初始化逻辑
子类可以重写此方法来实现特定的初始化操作 子类可以重写此方法来实现特定的初始化操作
""" """
logger.info(f'{self.PLUGIN_NAME} initialized.') logger.info(f'{self.PLUGIN_NAME} initialized.') # Zxy记录插件初始化日志
# Zxy注册插件钩子
def register_hooks(self): def register_hooks(self):
""" """
注册插件钩子 注册插件钩子
@ -29,13 +35,14 @@ class BasePlugin:
""" """
pass pass
# Zxy获取插件信息
def get_plugin_info(self): def get_plugin_info(self):
""" """
获取插件信息 获取插件信息
:return: 包含插件元数据的字典 :return: 包含插件元数据的字典
""" """
return { return {
'name': self.PLUGIN_NAME, 'name': self.PLUGIN_NAME, # Zxy插件名称
'description': self.PLUGIN_DESCRIPTION, 'description': self.PLUGIN_DESCRIPTION, # Zxy插件描述
'version': self.PLUGIN_VERSION 'version': self.PLUGIN_VERSION # Zxy插件版本
} }

@ -1,7 +1,8 @@
ARTICLE_DETAIL_LOAD = 'article_detail_load' # Zxy定义文章相关的钩子常量
ARTICLE_CREATE = 'article_create' ARTICLE_DETAIL_LOAD = 'article_detail_load' # Zxy文章详情加载时触发的钩子
ARTICLE_UPDATE = 'article_update' ARTICLE_CREATE = 'article_create' # Zxy文章创建时触发的钩子
ARTICLE_DELETE = 'article_delete' ARTICLE_UPDATE = 'article_update' # Zxy文章更新时触发的钩子
ARTICLE_DELETE = 'article_delete' # Zxy文章删除时触发的钩子
ARTICLE_CONTENT_HOOK_NAME = "the_content"
# Zxy定义文章内容处理的钩子名称
ARTICLE_CONTENT_HOOK_NAME = "the_content" # Zxy文章内容处理的钩子名称

@ -1,44 +1,47 @@
# Zxy导入日志模块
import logging import logging
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy定义全局钩子字典
_hooks = {} _hooks = {}
# Zxy注册钩子回调
def register(hook_name: str, callback: callable): def register(hook_name: str, callback: callable):
""" """
注册一个钩子回调 注册一个钩子回调
""" """
if hook_name not in _hooks: if hook_name not in _hooks: # Zxy检查钩子是否已存在
_hooks[hook_name] = [] _hooks[hook_name] = [] # Zxy初始化钩子列表
_hooks[hook_name].append(callback) _hooks[hook_name].append(callback) # Zxy添加回调到钩子列表
logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'") logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'") # Zxy记录注册日志
# Zxy执行Action Hook
def run_action(hook_name: str, *args, **kwargs): def run_action(hook_name: str, *args, **kwargs):
""" """
执行一个 Action Hook 执行一个 Action Hook
它会按顺序执行所有注册到该钩子上的回调函数 它会按顺序执行所有注册到该钩子上的回调函数
""" """
if hook_name in _hooks: if hook_name in _hooks: # Zxy检查钩子是否存在
logger.debug(f"Running action hook '{hook_name}'") logger.debug(f"Running action hook '{hook_name}'") # Zxy记录执行日志
for callback in _hooks[hook_name]: for callback in _hooks[hook_name]: # Zxy遍历钩子回调
try: try:
callback(*args, **kwargs) callback(*args, **kwargs) # Zxy执行回调
except Exception as e: except Exception as e:
logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) 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): def apply_filters(hook_name: str, value, *args, **kwargs):
""" """
执行一个 Filter Hook 执行一个 Filter Hook
它会把 value 依次传递给所有注册的回调函数进行处理 它会把 value 依次传递给所有注册的回调函数进行处理
""" """
if hook_name in _hooks: if hook_name in _hooks: # Zxy检查钩子是否存在
logger.debug(f"Applying filter hook '{hook_name}'") logger.debug(f"Applying filter hook '{hook_name}'") # Zxy记录执行日志
for callback in _hooks[hook_name]: for callback in _hooks[hook_name]: # Zxy遍历钩子回调
try: try:
value = callback(value, *args, **kwargs) value = callback(value, *args, **kwargs) # Zxy调用回调处理值
except Exception as e: except Exception as e:
logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) # Zxy记录错误日志
return value return value # Zxy返回处理后的值

@ -1,19 +1,24 @@
# Zxy导入os模块用于操作路径
import os import os
# Zxy导入日志模块
import logging import logging
# Zxy导入Django的配置模块
from django.conf import settings from django.conf import settings
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy动态加载插件
def load_plugins(): def load_plugins():
""" """
Dynamically loads and initializes plugins from the 'plugins' directory. Dynamically loads and initializes plugins from the 'plugins' directory.
This function is intended to be called when the Django app registry is ready. This function is intended to be called when the Django app registry is ready.
""" """
for plugin_name in settings.ACTIVE_PLUGINS: for plugin_name in settings.ACTIVE_PLUGINS: # Zxy遍历激活的插件
plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name) 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')): if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')): # Zxy检查插件目录和文件
try: try:
__import__(f'plugins.{plugin_name}.plugin') __import__(f'plugins.{plugin_name}.plugin') # Zxy动态导入插件模块
logger.info(f"Successfully loaded plugin: {plugin_name}") logger.info(f"Successfully loaded plugin: {plugin_name}") # Zxy记录加载成功日志
except ImportError as e: except ImportError as e:
logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e) logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e) # Zxy记录加载失败日志

@ -16,12 +16,13 @@ from pathlib import Path
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Zxy环境变量布尔值转换函数将环境变量转换为布尔值
def env_to_bool(env, default): def env_to_bool(env, default):
str_val = os.environ.get(env) str_val = os.environ.get(env)
return default if str_val is None else str_val == 'True' return default if str_val is None else str_val == 'True'
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Zxy项目根目录获取项目的绝对路径
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
@ -30,20 +31,20 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get( SECRET_KEY = os.environ.get(
'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6' '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! # Zxy调试模式从环境变量获取或默认为 True
DEBUG = env_to_bool('DJANGO_DEBUG', True) DEBUG = env_to_bool('DJANGO_DEBUG', True)
# DEBUG = False # Zxy测试模式检测检查是否运行测试
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test' TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
# ALLOWED_HOSTS = [] # Zxy允许的主机名允许访问的域名列表
ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com'] ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']
# django 4.0新增配置 # ZxyCSRF 可信来源:允许的 CSRF 来源
CSRF_TRUSTED_ORIGINS = ['http://example.com'] CSRF_TRUSTED_ORIGINS = ['http://example.com']
# Application definition # Application definition
# Zxy应用程序定义定义项目中使用的 Django 应用程序
INSTALLED_APPS = [ INSTALLED_APPS = [
# 'django.contrib.admin', # 默认的 Django 应用程序
'django.contrib.admin.apps.SimpleAdminConfig', 'django.contrib.admin.apps.SimpleAdminConfig',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -52,6 +53,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.sitemaps', 'django.contrib.sitemaps',
# 第三方和自定义应用程序
'mdeditor', 'mdeditor',
'haystack', 'haystack',
'blog', 'blog',
@ -63,7 +65,7 @@ INSTALLED_APPS = [
'compressor', 'compressor',
'djangoblog' 'djangoblog'
] ]
# Zxy中间件配置定义请求处理的中间件
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
@ -80,9 +82,9 @@ MIDDLEWARE = [
'django.middleware.http.ConditionalGetMiddleware', 'django.middleware.http.ConditionalGetMiddleware',
'blog.middleware.OnlineMiddleware' 'blog.middleware.OnlineMiddleware'
] ]
# ZxyURL 配置:定义项目的 URL 配置文件
ROOT_URLCONF = 'djangoblog.urls' ROOT_URLCONF = 'djangoblog.urls'
# Zxy模板配置配置 Django 模板引擎
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -99,29 +101,27 @@ TEMPLATES = [
}, },
}, },
] ]
# ZxyWSGI 应用程序:定义 WSGI 应用程序入口
WSGI_APPLICATION = 'djangoblog.wsgi.application' WSGI_APPLICATION = 'djangoblog.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
# Zxy数据库配置配置数据库连接
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('DJANGO_MYSQL_DATABASE') or 'djangoblog', 'NAME': 'djangoblog',
'USER': os.environ.get('DJANGO_MYSQL_USER') or 'root', 'USER': 'root',
'PASSWORD': os.environ.get('DJANGO_MYSQL_PASSWORD') or 'root', 'PASSWORD': '123456',
'HOST': os.environ.get('DJANGO_MYSQL_HOST') or '127.0.0.1', 'HOST': '127.0.0.1',
'PORT': int( 'PORT': 3306,
os.environ.get('DJANGO_MYSQL_PORT') or 3306), }
'OPTIONS': { }
'charset': 'utf8mb4'},
}}
# Password validation # Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
# Zxy密码验证器配置密码验证规则
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
@ -136,7 +136,7 @@ AUTH_PASSWORD_VALIDATORS = [
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
}, },
] ]
# Zxy语言和时区配置配置语言和时区
LANGUAGES = ( LANGUAGES = (
('en', _('English')), ('en', _('English')),
('zh-hans', _('Simplified Chinese')), ('zh-hans', _('Simplified Chinese')),
@ -159,7 +159,7 @@ USE_TZ = False
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
# ZxyHaystack 搜索配置:配置 Haystack 搜索引擎
HAYSTACK_CONNECTIONS = { HAYSTACK_CONNECTIONS = {
'default': { 'default': {
'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine', 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine',
@ -171,12 +171,12 @@ HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# Allow user login with username and password # Allow user login with username and password
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
'accounts.user_login_backend.EmailOrUsernameModelBackend'] 'accounts.user_login_backend.EmailOrUsernameModelBackend']
# Zxy静态文件配置配置静态文件路径
STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic') STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic')
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES = os.path.join(BASE_DIR, 'static') STATICFILES = os.path.join(BASE_DIR, 'static')
# Zxy用户模型配置定义自定义用户模型和登录 URL
AUTH_USER_MODEL = 'accounts.BlogUser' AUTH_USER_MODEL = 'accounts.BlogUser'
LOGIN_URL = '/login/' LOGIN_URL = '/login/'
@ -193,6 +193,7 @@ PAGINATE_BY = 10
# http cache timeout # http cache timeout
CACHE_CONTROL_MAX_AGE = 2592000 CACHE_CONTROL_MAX_AGE = 2592000
# cache setting # cache setting
# Zxy缓存配置配置缓存后端
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
@ -208,7 +209,7 @@ if os.environ.get("DJANGO_REDIS_URL"):
'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}', 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}',
} }
} }
# Zxy站点 ID定义当前站点的 ID
SITE_ID = 1 SITE_ID = 1
BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_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' or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn'
@ -232,7 +233,7 @@ WXADMIN = os.environ.get(
LOG_PATH = os.path.join(BASE_DIR, 'logs') LOG_PATH = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOG_PATH): if not os.path.exists(LOG_PATH):
os.makedirs(LOG_PATH, exist_ok=True) os.makedirs(LOG_PATH, exist_ok=True)
# Zxy日志配置配置日志记录
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': False, 'disable_existing_loggers': False,
@ -293,7 +294,7 @@ LOGGING = {
} }
} }
} }
# Zxy压缩配置配置静态文件压缩
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
@ -313,13 +314,13 @@ COMPRESS_CSS_FILTERS = [
COMPRESS_JS_FILTERS = [ COMPRESS_JS_FILTERS = [
'compressor.filters.jsmin.JSMinFilter' 'compressor.filters.jsmin.JSMinFilter'
] ]
# Zxy媒体文件配置配置媒体文件路径
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# ZxyElasticsearch 配置:配置 Elasticsearch 搜索引擎
if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
ELASTICSEARCH_DSL = { ELASTICSEARCH_DSL = {
'default': { 'default': {
@ -332,7 +333,7 @@ if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
}, },
} }
# Plugin System # Zxy插件系统配置定义插件目录和激活的插件
PLUGINS_DIR = BASE_DIR / 'plugins' PLUGINS_DIR = BASE_DIR / 'plugins'
ACTIVE_PLUGINS = [ ACTIVE_PLUGINS = [
'article_copyright', 'article_copyright',

@ -1,59 +1,70 @@
# Zxy导入 Django 的 Sitemap 模块和其他相关模块
from django.contrib.sitemaps import Sitemap from django.contrib.sitemaps import Sitemap
from django.urls import reverse from django.urls import reverse
from blog.models import Article, Category, Tag from blog.models import Article, Category, Tag
# Zxy定义静态页面的 Sitemap
class StaticViewSitemap(Sitemap): class StaticViewSitemap(Sitemap):
priority = 0.5 priority = 0.5 # Zxy设置优先级
changefreq = 'daily' changefreq = 'daily' # Zxy设置更新频率
def items(self): def items(self):
# Zxy返回静态页面的 URL 名称
return ['blog:index', ] return ['blog:index', ]
def location(self, item): def location(self, item):
# Zxy通过 URL 名称生成完整的 URL
return reverse(item) return reverse(item)
# Zxy定义文章的 Sitemap
class ArticleSiteMap(Sitemap): class ArticleSiteMap(Sitemap):
changefreq = "monthly" changefreq = "monthly" # Zxy文章更新频率为每月
priority = "0.6" priority = "0.6" # Zxy文章优先级
def items(self): def items(self):
# Zxy返回所有已发布的文章
return Article.objects.filter(status='p') return Article.objects.filter(status='p')
def lastmod(self, obj): def lastmod(self, obj):
# Zxy返回文章的最后修改时间
return obj.last_modify_time return obj.last_modify_time
# Zxy定义分类的 Sitemap
class CategorySiteMap(Sitemap): class CategorySiteMap(Sitemap):
changefreq = "Weekly" changefreq = "Weekly" # Zxy分类更新频率为每周
priority = "0.6" priority = "0.6" # Zxy分类优先级
def items(self): def items(self):
# Zxy返回所有分类
return Category.objects.all() return Category.objects.all()
def lastmod(self, obj): def lastmod(self, obj):
# Zxy返回分类的最后修改时间
return obj.last_modify_time return obj.last_modify_time
# Zxy定义标签的 Sitemap
class TagSiteMap(Sitemap): class TagSiteMap(Sitemap):
changefreq = "Weekly" changefreq = "Weekly" # Zxy标签更新频率为每周
priority = "0.3" priority = "0.3" # Zxy标签优先级
def items(self): def items(self):
# Zxy返回所有标签
return Tag.objects.all() return Tag.objects.all()
def lastmod(self, obj): def lastmod(self, obj):
# Zxy返回标签的最后修改时间
return obj.last_modify_time return obj.last_modify_time
# Zxy定义用户的 Sitemap
class UserSiteMap(Sitemap): class UserSiteMap(Sitemap):
changefreq = "Weekly" changefreq = "Weekly" # Zxy用户更新频率为每周
priority = "0.3" priority = "0.3" # Zxy用户优先级
def items(self): def items(self):
# Zxy返回所有用户的作者列表去重
return list(set(map(lambda x: x.author, Article.objects.all()))) return list(set(map(lambda x: x.author, Article.objects.all())))
def lastmod(self, obj): def lastmod(self, obj):
return obj.date_joined # Zxy返回用户的注册时间
return obj.date_joined

@ -1,21 +1,26 @@
# Zxy导入日志模块
import logging import logging
# Zxy导入 requests 模块用于发送 HTTP 请求
import requests import requests
from django.conf import settings from django.conf import settings
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy定义爬虫通知类
class SpiderNotify(): class SpiderNotify():
@staticmethod @staticmethod
def baidu_notify(urls): def baidu_notify(urls):
# Zxy向百度站长平台发送 URL 提交请求
try: try:
data = '\n'.join(urls) data = '\n'.join(urls) # Zxy将 URL 列表拼接为字符串
result = requests.post(settings.BAIDU_NOTIFY_URL, data=data) result = requests.post(settings.BAIDU_NOTIFY_URL, data=data) # Zxy发送 POST 请求
logger.info(result.text) logger.info(result.text) # Zxy记录响应内容
except Exception as e: except Exception as e:
logger.error(e) logger.error(e) # Zxy记录异常信息
@staticmethod @staticmethod
def notify(url): def notify(url):
SpiderNotify.baidu_notify(url) # Zxy调用百度通知方法
SpiderNotify.baidu_notify(url)

@ -1,16 +1,21 @@
# Zxy导入 Django 的测试模块
from django.test import TestCase from django.test import TestCase
# Zxy导入项目中的工具函数
from djangoblog.utils import * from djangoblog.utils import *
# Zxy定义测试类
class DjangoBlogTest(TestCase): class DjangoBlogTest(TestCase):
def setUp(self): def setUp(self):
# Zxy测试初始化方法暂无内容
pass pass
def test_utils(self): def test_utils(self):
md5 = get_sha256('test') # Zxy测试工具函数
self.assertIsNotNone(md5) md5 = get_sha256('test') # Zxy测试 SHA256 函数
c = CommonMarkdown.get_markdown(''' self.assertIsNotNone(md5) # Zxy断言返回值不为空
c = CommonMarkdown.get_markdown(''' # Zxy测试 Markdown 转换
# Title1 # Title1
```python ```python
@ -23,10 +28,11 @@ class DjangoBlogTest(TestCase):
''') ''')
self.assertIsNotNone(c) self.assertIsNotNone(c) # Zxy断言返回值不为空
d = { d = {
'd': 'key1', 'd': 'key1',
'd2': 'key2' 'd2': 'key2'
} }
data = parse_dict_to_url(d) data = parse_dict_to_url(d) # Zxy测试字典转 URL 函数
self.assertIsNotNone(data) self.assertIsNotNone(data) # Zxy断言返回值不为空

@ -1,3 +1,4 @@
# Zxy定义项目的 URL 配置
"""djangoblog URL Configuration """djangoblog URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see: The `urlpatterns` list routes URLs to views. For more information please see:
@ -27,8 +28,8 @@ from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm
from djangoblog.feeds import DjangoBlogFeed from djangoblog.feeds import DjangoBlogFeed
from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap
# Zxy定义站点地图
sitemaps = { sitemaps = {
'blog': ArticleSiteMap, 'blog': ArticleSiteMap,
'Category': CategorySiteMap, 'Category': CategorySiteMap,
'Tag': TagSiteMap, 'Tag': TagSiteMap,
@ -36,29 +37,32 @@ sitemaps = {
'static': StaticViewSitemap 'static': StaticViewSitemap
} }
# Zxy定义 404、500 和 403 错误页面的视图
handler404 = 'blog.views.page_not_found_view' handler404 = 'blog.views.page_not_found_view'
handler500 = 'blog.views.server_error_view' handler500 = 'blog.views.server_error_view'
handle403 = 'blog.views.permission_denied_view' handle403 = 'blog.views.permission_denied_view'
# Zxy定义 URL 模式
urlpatterns = [ urlpatterns = [
path('i18n/', include('django.conf.urls.i18n')), path('i18n/', include('django.conf.urls.i18n')), # Zxy国际化语言切换
] ]
urlpatterns += i18n_patterns( urlpatterns += i18n_patterns(
re_path(r'^admin/', admin_site.urls), re_path(r'^admin/', admin_site.urls), # Zxy自定义 Admin 站点
re_path(r'', include('blog.urls', namespace='blog')), re_path(r'', include('blog.urls', namespace='blog')), # Zxy博客应用的 URL
re_path(r'mdeditor/', include('mdeditor.urls')), re_path(r'mdeditor/', include('mdeditor.urls')), # Zxy Markdown 编辑器的 URL
re_path(r'', include('comments.urls', namespace='comment')), re_path(r'', include('comments.urls', namespace='comment')), # Zxy评论应用的 URL
re_path(r'', include('accounts.urls', namespace='account')), re_path(r'', include('accounts.urls', namespace='account')), # Zxy用户账户的 URL
re_path(r'', include('oauth.urls', namespace='oauth')), re_path(r'', include('oauth.urls', namespace='oauth')), # Zxy OAuth 应用的 URL
re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, # Zxy站点地图
name='django.contrib.sitemaps.views.sitemap'), name='django.contrib.sitemaps.views.sitemap'),
re_path(r'^feed/$', DjangoBlogFeed()), re_path(r'^feed/$', DjangoBlogFeed()), # Zxy RSS 订阅
re_path(r'^rss/$', DjangoBlogFeed()), re_path(r'^rss/$', DjangoBlogFeed()), # Zxy RSS 订阅
re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm), re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm), # Zxy搜索功能
name='search'), name='search'),
re_path(r'', include('servermanager.urls', namespace='servermanager')), re_path(r'', include('servermanager.urls', namespace='servermanager')), # Zxy服务器管理应用的 URL
re_path(r'', include('owntracks.urls', namespace='owntracks')) re_path(r'', include('owntracks.urls', namespace='owntracks')), # Zxy位置跟踪应用的 URL
, prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) prefix_default_language=False
) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) # Zxy静态文件的 URL
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, urlpatterns += static(settings.MEDIA_URL, # Zxy媒体文件的 URL
document_root=settings.MEDIA_ROOT) document_root=settings.MEDIA_ROOT)

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
# Zxy导入日志模块
import logging import logging
import os import os
import random import random
@ -17,20 +17,21 @@ from django.contrib.sites.models import Site
from django.core.cache import cache from django.core.cache import cache
from django.templatetags.static import static from django.templatetags.static import static
# Zxy获取日志记录器
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Zxy获取文章和评论的最大 ID
def get_max_articleid_commentid(): def get_max_articleid_commentid():
from blog.models import Article from blog.models import Article
from comments.models import Comment from comments.models import Comment
return (Article.objects.latest().pk, Comment.objects.latest().pk) return (Article.objects.latest().pk, Comment.objects.latest().pk)
# Zxy计算字符串的 SHA256 值
def get_sha256(str): def get_sha256(str):
m = sha256(str.encode('utf-8')) m = sha256(str.encode('utf-8'))
return m.hexdigest() return m.hexdigest()
# Zxy缓存装饰器用于缓存函数的返回值
def cache_decorator(expiration=3 * 60): def cache_decorator(expiration=3 * 60):
def wrapper(func): def wrapper(func):
def news(*args, **kwargs): def news(*args, **kwargs):
@ -41,41 +42,27 @@ def cache_decorator(expiration=3 * 60):
key = None key = None
if not key: if not key:
unique_str = repr((func, args, kwargs)) unique_str = repr((func, args, kwargs))
m = sha256(unique_str.encode('utf-8')) m = sha256(unique_str.encode('utf-8'))
key = m.hexdigest() key = m.hexdigest()
value = cache.get(key) value = cache.get(key)
if value is not None: if value is not None:
# logger.info('cache_decorator get cache:%s key:%s' % (func.__name__, key))
if str(value) == '__default_cache_value__': if str(value) == '__default_cache_value__':
return None return None
else: else:
return value return value
else: else:
logger.debug( logger.debug('cache_decorator set cache:%s key:%s' % (func.__name__, key))
'cache_decorator set cache:%s key:%s' %
(func.__name__, key))
value = func(*args, **kwargs) value = func(*args, **kwargs)
if value is None: if value is None:
cache.set(key, '__default_cache_value__', expiration) cache.set(key, '__default_cache_value__', expiration)
else: else:
cache.set(key, value, expiration) cache.set(key, value, expiration)
return value return value
return news return news
return wrapper return wrapper
# Zxy刷新视图缓存
def expire_view_cache(path, servername, serverport, key_prefix=None): def expire_view_cache(path, servername, serverport, key_prefix=None):
'''
刷新视图缓存
:param path:url路径
:param servername:host
:param serverport:端口
:param key_prefix:前缀
:return:是否成功
'''
from django.http import HttpRequest from django.http import HttpRequest
from django.utils.cache import get_cache_key from django.utils.cache import get_cache_key
@ -91,24 +78,17 @@ def expire_view_cache(path, servername, serverport, key_prefix=None):
return True return True
return False return False
# Zxy获取当前站点
@cache_decorator() @cache_decorator()
def get_current_site(): def get_current_site():
site = Site.objects.get_current() site = Site.objects.get_current()
return site return site
# Zxy定义 CommonMarkdown 类,用于处理 Markdown 转换
class CommonMarkdown: class CommonMarkdown:
@staticmethod @staticmethod
def _convert_markdown(value): def _convert_markdown(value):
md = markdown.Markdown( md = markdown.Markdown(extensions=['extra', 'codehilite', 'toc', 'tables'])
extensions=[
'extra',
'codehilite',
'toc',
'tables',
]
)
body = md.convert(value) body = md.convert(value)
toc = md.toc toc = md.toc
return body, toc return body, toc
@ -123,28 +103,22 @@ class CommonMarkdown:
body, toc = CommonMarkdown._convert_markdown(value) body, toc = CommonMarkdown._convert_markdown(value)
return body return body
# Zxy发送邮件
def send_email(emailto, title, content): def send_email(emailto, title, content):
from djangoblog.blog_signals import send_email_signal from djangoblog.blog_signals import send_email_signal
send_email_signal.send( send_email_signal.send(send_email.__class__, emailto=emailto, title=title, content=content)
send_email.__class__,
emailto=emailto,
title=title,
content=content)
# Zxy生成随机验证码
def generate_code() -> str: def generate_code() -> str:
"""生成随机数验证码"""
return ''.join(random.sample(string.digits, 6)) return ''.join(random.sample(string.digits, 6))
# Zxy将字典转换为 URL 参数
def parse_dict_to_url(dict): def parse_dict_to_url(dict):
from urllib.parse import quote from urllib.parse import quote
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/')) url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/')) for k, v in dict.items()])
for k, v in dict.items()])
return url return url
# Zxy获取博客设置
def get_blog_setting(): def get_blog_setting():
value = cache.get('get_blog_setting') value = cache.get('get_blog_setting')
if value: if value:
@ -172,22 +146,15 @@ def get_blog_setting():
cache.set('get_blog_setting', value) cache.set('get_blog_setting', value)
return value return value
# Zxy保存用户头像
def save_user_avatar(url): def save_user_avatar(url):
'''
保存用户头像
:param url:头像url
:return: 本地路径
'''
logger.info(url) logger.info(url)
try: try:
basedir = os.path.join(settings.STATICFILES, 'avatar') basedir = os.path.join(settings.STATICFILES, 'avatar')
rsp = requests.get(url, timeout=2) rsp = requests.get(url, timeout=2)
if rsp.status_code == 200: if rsp.status_code == 200:
if not os.path.exists(basedir): if not os.path.exists(basedir):
os.makedirs(basedir) os.makedirs(basedir)
image_extensions = ['.jpg', '.png', 'jpeg', '.gif'] image_extensions = ['.jpg', '.png', 'jpeg', '.gif']
isimage = len([i for i in image_extensions if url.endswith(i)]) > 0 isimage = len([i for i in image_extensions if url.endswith(i)]) > 0
ext = os.path.splitext(url)[1] if isimage else '.jpg' ext = os.path.splitext(url)[1] if isimage else '.jpg'
@ -200,7 +167,7 @@ def save_user_avatar(url):
logger.error(e) logger.error(e)
return static('blog/img/avatar.png') return static('blog/img/avatar.png')
# Zxy删除侧边栏缓存
def delete_sidebar_cache(): def delete_sidebar_cache():
from blog.models import LinkShowType from blog.models import LinkShowType
keys = ["sidebar" + x for x in LinkShowType.values] keys = ["sidebar" + x for x in LinkShowType.values]
@ -208,13 +175,13 @@ def delete_sidebar_cache():
logger.info('delete sidebar key:' + k) logger.info('delete sidebar key:' + k)
cache.delete(k) cache.delete(k)
# Zxy删除视图缓存
def delete_view_cache(prefix, keys): def delete_view_cache(prefix, keys):
from django.core.cache.utils import make_template_fragment_key from django.core.cache.utils import make_template_fragment_key
key = make_template_fragment_key(prefix, keys) key = make_template_fragment_key(prefix, keys)
cache.delete(key) cache.delete(key)
# Zxy获取资源 URL
def get_resource_url(): def get_resource_url():
if settings.STATIC_URL: if settings.STATIC_URL:
return settings.STATIC_URL return settings.STATIC_URL
@ -222,11 +189,10 @@ def get_resource_url():
site = get_current_site() site = get_current_site()
return 'http://' + site.domain + '/static/' return 'http://' + site.domain + '/static/'
# Zxy定义允许的 HTML 标签和属性
ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1', ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1', 'h2', 'p']
'h2', 'p']
ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']} ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
# Zxy清理 HTML 内容
def sanitize_html(html): def sanitize_html(html):
return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES) return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)

File diff suppressed because it is too large Load Diff

@ -7,10 +7,14 @@ For more information on this file, see
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
""" """
# Zxy导入os模块用于操作环境变量
import os import os
# Zxy从Django中导入get_wsgi_application函数用于获取WSGI应用
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
# Zxy设置环境变量DJANGO_SETTINGS_MODULE指定Django项目的配置文件
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
application = get_wsgi_application() # Zxy获取WSGI应用实例用于部署
application = get_wsgi_application()
Loading…
Cancel
Save