Compare commits

...

3 Commits

@ -5,6 +5,8 @@ from abc import abstractmethod
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
@ -177,39 +179,64 @@ class Article(BaseModel):
return ""
#zbw 模块功能定义文章分类模型Category继承自BaseModel用于存储博客系统中的文章分类信息
#zbw 支持分类的层级结构父分类与子分类提供获取分类绝对URL、分类树含父级及子分类的方法
#zbw 优化了缓存策略、添加循环引用检测并移除冗余代码,提升了模型的健壮性和性能
class Category(BaseModel):
"""文章分类"""
#zbw 分类名称字符类型最大长度30唯一不允许重名使用翻译函数支持多语言
name = models.CharField(_('category name'), max_length=30, unique=True)
#zbw 父分类,自关联外键,允许为空(表示顶级分类),级联删除(父分类删除时子分类也删除)
parent_category = models.ForeignKey(
'self',
verbose_name=_('parent category'),
blank=True,
null=True,
on_delete=models.CASCADE)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
#zbw URL别名用于生成友好URL默认值'no-slug'设置唯一约束避免URL冲突
slug = models.SlugField(default='no-slug', max_length=60, blank=True, unique=True)
#zbw 排序索引数值越小越靠前默认0用于控制分类展示顺序
index = models.IntegerField(default=0, verbose_name=_('index'))
class Meta:
#zbw 按index降序排列数值小的在前若index相同则按默认主键排序
ordering = ['-index']
verbose_name = _('category')
verbose_name_plural = verbose_name
verbose_name_plural = verbose_name # 复数形式与单数相同
def clean(self):
#zbw 验证逻辑检测分类是否形成循环引用如A的父级是BB的父级是A
current = self
while current.parent_category:
if current.parent_category == self:
raise ValidationError(_("Category cannot form a circular reference"))
current = current.parent_category
def save(self, *args, **kwargs):
#zbw 保存前先执行验证逻辑,确保数据合法性
self.full_clean()
super().save(*args, **kwargs)
#zbw 方法功能获取分类详情页的绝对URL
#zbw 返回值反向解析blog应用的category_detail视图参数为当前分类的slug
def get_absolute_url(self):
return reverse(
'blog:category_detail', kwargs={
'category_name': self.slug})
#zbw 方法功能:定义对象的字符串表示形式
#zbw 返回值分类名称便于在admin后台及调试时识别对象
def __str__(self):
return self.name
@cache_decorator(60 * 60 * 10)
#zbw 方法功能:递归获取当前分类的所有父级分类(含自身),形成分类树
#zbw 缓存装饰器结果缓存1小时平衡性能与数据新鲜度减少数据库查询
#zbw 返回值list of Category objects从当前分类到顶级分类的有序列表
@cache_decorator(60 * 60 * 1)
def get_category_tree(self):
"""
递归获得分类目录的父级
:return:
"""
categorys = []
#zbw 内部递归函数:将分类添加到列表,若有父分类则继续递归
def parse(category):
categorys.append(category)
if category.parent_category:
@ -218,28 +245,39 @@ class Category(BaseModel):
parse(self)
return categorys
@cache_decorator(60 * 60 * 10)
#zbw 方法功能:获取当前分类的所有子分类(含自身)
#zbw 缓存装饰器结果缓存1小时优化性能
#zbw 返回值list of Category objects包含当前分类及所有子孙分类
@cache_decorator(60 * 60 * 1)
def get_sub_categorys(self):
"""
获得当前分类目录所有子集
:return:
"""
categorys = []
#zbw 一次性查询所有分类避免递归中多次查询数据库N+1问题优化
all_categorys = Category.objects.all()
#zbw 内部递归函数:递归获取所有子分类,移除冗余判断
def parse(category):
if category not in categorys:
categorys.append(category)
#zbw 从已查询的所有分类中筛选子分类,减少数据库交互
childs = all_categorys.filter(parent_category=category)
for child in childs:
if category not in categorys:
categorys.append(child)
parse(child)
parse(child) # 直接递归子分类,内部已做去重判断
parse(self)
return categorys
#zbw 信号处理:当分类保存(新增/更新清除相关缓存需缓存装饰器支持key生成逻辑
@receiver(post_save, sender=Category) # type: ignore
def clear_category_cache(sender, instance, **kwargs):
#zbw 清除当前分类的缓存
instance.get_category_tree.clear_cache()
instance.get_sub_categorys.clear_cache()
#zbw 清除父分类的子分类缓存(因当前分类变化可能影响父分类的子树)
if instance.parent_category:
instance.parent_category.get_sub_categorys.clear_cache()
class Tag(BaseModel):
"""文章标签"""
name = models.CharField(_('tag name'), max_length=30, unique=True)

Binary file not shown.

@ -13,8 +13,8 @@ from oauth.admin import *
from oauth.models import *
from owntracks.admin import *
from owntracks.models import *
from servermanager.admin import *
from servermanager.models import *
# from servermanager.admin import * # 暂时禁用
# from servermanager.models import * # 暂时禁用
class DjangoBlogAdminSite(AdminSite):
@ -47,8 +47,8 @@ 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(commands, CommandsAdmin) # 暂时禁用
# admin_site.register(EmailSendLog, EmailSendLogAdmin) # 暂时禁用
admin_site.register(BlogUser, BlogUserAdmin)

@ -58,7 +58,7 @@ INSTALLED_APPS = [
'accounts',
'comments',
'oauth',
'servermanager',
# 'servermanager', # 暂时禁用因为缺少werobot模块
'owntracks',
'compressor',
'djangoblog'
@ -108,14 +108,8 @@ WSGI_APPLICATION = 'djangoblog.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoblog',
'USER': 'root',
'PASSWORD':'root',
'HOST': '127.0.0.1',
'PORT': 3306,
'OPTIONS': {
'charset': 'utf8mb4'},
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}}
# Password validation

@ -56,7 +56,7 @@ urlpatterns += i18n_patterns(
re_path(r'^rss/$', DjangoBlogFeed()),
re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm),
name='search'),
re_path(r'', include('servermanager.urls', namespace='servermanager')),
# re_path(r'', include('servermanager.urls', namespace='servermanager')), # 暂时禁用
re_path(r'', include('owntracks.urls', namespace='owntracks'))
, prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:

@ -0,0 +1,12 @@
import os
import traceback
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
try:
from djangoblog.wsgi import application
print("WSGI application loaded successfully!")
except Exception as e:
print(f"Error loading WSGI application: {e}")
print("Detailed traceback:")
traceback.print_exc()
Loading…
Cancel
Save