Compare commits

...

3 Commits

@ -5,6 +5,8 @@ from abc import abstractmethod
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models 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.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -177,39 +179,64 @@ class Article(BaseModel):
return "" return ""
#zbw 模块功能定义文章分类模型Category继承自BaseModel用于存储博客系统中的文章分类信息
#zbw 支持分类的层级结构父分类与子分类提供获取分类绝对URL、分类树含父级及子分类的方法
#zbw 优化了缓存策略、添加循环引用检测并移除冗余代码,提升了模型的健壮性和性能
class Category(BaseModel): class Category(BaseModel):
"""文章分类""" """文章分类"""
#zbw 分类名称字符类型最大长度30唯一不允许重名使用翻译函数支持多语言
name = models.CharField(_('category name'), max_length=30, unique=True) name = models.CharField(_('category name'), max_length=30, unique=True)
#zbw 父分类,自关联外键,允许为空(表示顶级分类),级联删除(父分类删除时子分类也删除)
parent_category = models.ForeignKey( parent_category = models.ForeignKey(
'self', 'self',
verbose_name=_('parent category'), verbose_name=_('parent category'),
blank=True, blank=True,
null=True, null=True,
on_delete=models.CASCADE) 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')) index = models.IntegerField(default=0, verbose_name=_('index'))
class Meta: class Meta:
#zbw 按index降序排列数值小的在前若index相同则按默认主键排序
ordering = ['-index'] ordering = ['-index']
verbose_name = _('category') 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): def get_absolute_url(self):
return reverse( return reverse(
'blog:category_detail', kwargs={ 'blog:category_detail', kwargs={
'category_name': self.slug}) 'category_name': self.slug})
#zbw 方法功能:定义对象的字符串表示形式
#zbw 返回值分类名称便于在admin后台及调试时识别对象
def __str__(self): def __str__(self):
return self.name 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): def get_category_tree(self):
"""
递归获得分类目录的父级
:return:
"""
categorys = [] categorys = []
#zbw 内部递归函数:将分类添加到列表,若有父分类则继续递归
def parse(category): def parse(category):
categorys.append(category) categorys.append(category)
if category.parent_category: if category.parent_category:
@ -218,28 +245,39 @@ class Category(BaseModel):
parse(self) parse(self)
return categorys 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): def get_sub_categorys(self):
"""
获得当前分类目录所有子集
:return:
"""
categorys = [] categorys = []
#zbw 一次性查询所有分类避免递归中多次查询数据库N+1问题优化
all_categorys = Category.objects.all() all_categorys = Category.objects.all()
#zbw 内部递归函数:递归获取所有子分类,移除冗余判断
def parse(category): def parse(category):
if category not in categorys: if category not in categorys:
categorys.append(category) categorys.append(category)
#zbw 从已查询的所有分类中筛选子分类,减少数据库交互
childs = all_categorys.filter(parent_category=category) childs = all_categorys.filter(parent_category=category)
for child in childs: for child in childs:
if category not in categorys: parse(child) # 直接递归子分类,内部已做去重判断
categorys.append(child)
parse(child)
parse(self) parse(self)
return categorys 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): class Tag(BaseModel):
"""文章标签""" """文章标签"""
name = models.CharField(_('tag name'), max_length=30, unique=True) 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 oauth.models import *
from owntracks.admin import * from owntracks.admin import *
from owntracks.models import * from owntracks.models import *
from servermanager.admin import * # from servermanager.admin import * # 暂时禁用
from servermanager.models import * # from servermanager.models import * # 暂时禁用
class DjangoBlogAdminSite(AdminSite): class DjangoBlogAdminSite(AdminSite):
@ -47,8 +47,8 @@ admin_site.register(Links, LinksAdmin)
admin_site.register(SideBar, SideBarAdmin) admin_site.register(SideBar, SideBarAdmin)
admin_site.register(BlogSettings, BlogSettingsAdmin) admin_site.register(BlogSettings, BlogSettingsAdmin)
admin_site.register(commands, CommandsAdmin) # admin_site.register(commands, CommandsAdmin) # 暂时禁用
admin_site.register(EmailSendLog, EmailSendLogAdmin) # admin_site.register(EmailSendLog, EmailSendLogAdmin) # 暂时禁用
admin_site.register(BlogUser, BlogUserAdmin) admin_site.register(BlogUser, BlogUserAdmin)

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

@ -56,7 +56,7 @@ urlpatterns += i18n_patterns(
re_path(r'^rss/$', DjangoBlogFeed()), re_path(r'^rss/$', DjangoBlogFeed()),
re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm), re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm),
name='search'), 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')) re_path(r'', include('owntracks.urls', namespace='owntracks'))
, 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)
if settings.DEBUG: 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