Delete 'src/blog/models_annotated_yjy.py'

main
piehnab5t 4 months ago
parent bbe9c7fc0f
commit a1f1c1265d

@ -1,301 +0,0 @@
import logging
import re
from abc import abstractmethod
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from mdeditor.fields import MDTextField # Markdown编辑器字段
from uuslug import slugify # 智能slug生成
from djangoblog.utils import cache_decorator, cache
from djangoblog.utils import get_current_site
logger = logging.getLogger(__name__)
class LinkShowType(models.TextChoices):
"""链接显示类型枚举"""
I = ('i', _('index')) # 首页
L = ('l', _('list')) # 列表页
P = ('p', _('post')) # 文章页
A = ('a', _('all')) # 全站
S = ('s', _('slide')) # 幻灯片
class BaseModel(models.Model):
"""抽象基模型,提供通用字段和方法"""
id = models.AutoField(primary_key=True)
creation_time = models.DateTimeField(_('creation time'), default=now)
last_modify_time = models.DateTimeField(_('modify time'), default=now)
def save(self, *args, **kwargs):
# 特殊处理文章浏览量的更新,避免触发其他逻辑
is_update_views = isinstance(
self,
Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
if is_update_views:
Article.objects.filter(pk=self.pk).update(views=self.views)
else:
# 自动生成slug基于title或name字段
if 'slug' in self.__dict__:
slug = getattr(
self, 'title') if 'title' in self.__dict__ else getattr(
self, 'name')
setattr(self, 'slug', slugify(slug))
super().save(*args, **kwargs)
def get_full_url(self):
"""获取完整URL包含域名"""
site = get_current_site().domain
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
return url
class Meta:
abstract = True # 抽象基类,不会创建数据库表
@abstractmethod
def get_absolute_url(self):
"""抽象方法子类必须实现获取绝对URL的方法"""
pass
class Article(BaseModel):
"""文章模型 - 核心内容模型"""
# 状态选择项
STATUS_CHOICES = (
('d', _('Draft')), # 草稿
('p', _('Published')), # 已发布
)
COMMENT_STATUS = (
('o', _('Open')), # 开放评论
('c', _('Close')), # 关闭评论
)
TYPE = (
('a', _('Article')), # 普通文章
('p', _('Page')), # 页面
)
title = models.CharField(_('title'), max_length=200, unique=True)
body = MDTextField(_('body')) # Markdown内容字段
pub_time = models.DateTimeField(_('publish time'), blank=False, null=False, default=now)
status = models.CharField(_('status'), max_length=1, choices=STATUS_CHOICES, default='p')
comment_status = models.CharField(_('comment status'), max_length=1, choices=COMMENT_STATUS, default='o')
type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a')
views = models.PositiveIntegerField(_('views'), default=0) # 浏览量统计
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('author'), on_delete=models.CASCADE)
article_order = models.IntegerField(_('order'), blank=False, null=False, default=0) # 文章排序
show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) # 是否显示目录
category = models.ForeignKey('Category', verbose_name=_('category'), on_delete=models.CASCADE)
tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) # 多对多标签关系
class Meta:
ordering = ['-article_order', '-pub_time'] # 默认排序按order倒序发布时间倒序
verbose_name = _('article')
verbose_name_plural = verbose_name
get_latest_by = 'id'
def get_absolute_url(self):
"""生成文章详情页URL包含年月日信息用于SEO"""
return reverse('blog:detailbyid', kwargs={
'article_id': self.id,
'year': self.creation_time.year,
'month': self.creation_time.month,
'day': self.creation_time.day
})
@cache_decorator(60 * 60 * 10) # 缓存10小时
def get_category_tree(self):
"""获取分类树结构,用于面包屑导航"""
tree = self.category.get_category_tree()
names = list(map(lambda c: (c.name, c.get_absolute_url()), tree))
return names
def viewed(self):
"""增加浏览量优化更新只更新views字段"""
self.views += 1
self.save(update_fields=['views'])
def comment_list(self):
"""获取文章评论列表(带缓存)"""
cache_key = 'article_comments_{id}'.format(id=self.id)
value = cache.get(cache_key)
if value:
logger.info('get article comments:{id}'.format(id=self.id))
return value
else:
comments = self.comment_set.filter(is_enable=True).order_by('-id')
cache.set(cache_key, comments, 60 * 100) # 缓存100分钟
return comments
def get_admin_url(self):
"""获取后台管理URL"""
info = (self._meta.app_label, self._meta.model_name)
return reverse('admin:%s_%s_change' % info, args=(self.pk,))
@cache_decorator(expiration=60 * 100)
def next_article(self):
"""下一篇已发布文章(带缓存)"""
return Article.objects.filter(
id__gt=self.id, status='p').order_by('id').first()
@cache_decorator(expiration=60 * 100)
def prev_article(self):
"""上一篇已发布文章(带缓存)"""
return Article.objects.filter(id__lt=self.id, status='p').first()
def get_first_image_url(self):
"""从文章内容中提取第一张图片URL用于缩略图等"""
match = re.search(r'!\[.*?\]\((.+?)\)', self.body) # 匹配Markdown图片语法
if match:
return match.group(1)
return ""
class Category(BaseModel):
"""文章分类模型,支持多级分类"""
name = models.CharField(_('category name'), max_length=30, unique=True)
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)
index = models.IntegerField(default=0, verbose_name=_('index')) # 分类排序
class Meta:
ordering = ['-index'] # 按index倒序排列
verbose_name = _('category')
verbose_name_plural = verbose_name
@cache_decorator(60 * 60 * 10)
def get_category_tree(self):
"""
递归获得分类目录的父级用于构建面包屑导航
:return: 分类树列表从当前分类到根分类
"""
categorys = []
def parse(category):
categorys.append(category)
if category.parent_category:
parse(category.parent_category)
parse(self)
return categorys
@cache_decorator(60 * 60 * 10)
def get_sub_categorys(self):
"""
获得当前分类目录所有子集用于分类导航
:return: 所有子分类列表
"""
categorys = []
all_categorys = Category.objects.all()
def parse(category):
if category not in categorys:
categorys.append(category)
childs = all_categorys.filter(parent_category=category)
for child in childs:
if category not in categorys:
categorys.append(child)
parse(child)
parse(self)
return categorys
class Tag(BaseModel):
"""文章标签模型"""
name = models.CharField(_('tag name'), max_length=30, unique=True)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
@cache_decorator(60 * 60 * 10)
def get_article_count(self):
"""获取标签下文章数量(带缓存)"""
return Article.objects.filter(tags__name=self.name).distinct().count()
class Meta:
ordering = ['name'] # 按标签名排序
verbose_name = _('tag')
verbose_name_plural = verbose_name
class Links(models.Model):
"""友情链接模型"""
name = models.CharField(_('link name'), max_length=30, unique=True)
link = models.URLField(_('link'))
sequence = models.IntegerField(_('order'), unique=True) # 链接显示顺序
is_enable = models.BooleanField(_('is show'), default=True)
show_type = models.CharField(_('show type'), max_length=1,
choices=LinkShowType.choices, default=LinkShowType.I) # 链接显示位置
creation_time = models.DateTimeField(_('creation time'), default=now)
last_mod_time = models.DateTimeField(_('modify time'), default=now)
class Meta:
ordering = ['sequence'] # 按sequence顺序排列
verbose_name = _('link')
verbose_name_plural = verbose_name
class SideBar(models.Model):
"""侧边栏模型可以展示自定义HTML内容"""
name = models.CharField(_('title'), max_length=100)
content = models.TextField(_('content')) # HTML内容
sequence = models.IntegerField(_('order'), unique=True) # 显示顺序
is_enable = models.BooleanField(_('is enable'), default=True)
creation_time = models.DateTimeField(_('creation time'), default=now)
last_mod_time = models.DateTimeField(_('modify time'), default=now)
class Meta:
ordering = ['sequence']
verbose_name = _('sidebar')
verbose_name_plural = verbose_name
class BlogSettings(models.Model):
"""博客全局配置模型(单例模式)"""
site_name = models.CharField(_('site name'), max_length=200, default='')
site_description = models.TextField(_('site description'), max_length=1000, default='')
site_seo_description = models.TextField(_('site seo description'), max_length=1000, default='')
site_keywords = models.TextField(_('site keywords'), max_length=1000, default='')
# 显示设置
article_sub_length = models.IntegerField(_('article sub length'), default=300) # 文章摘要长度
sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10)
sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5)
article_comment_count = models.IntegerField(_('article comment count'), default=5)
# 广告设置
show_google_adsense = models.BooleanField(_('show adsense'), default=False)
google_adsense_codes = models.TextField(_('adsense code'), max_length=2000, default='')
# 评论设置
open_site_comment = models.BooleanField(_('open site comment'), default=True)
comment_need_review = models.BooleanField('评论是否需要审核', default=False)
# 代码注入和备案
global_header = models.TextField("公共头部", default='') # 全局HTML头部
global_footer = models.TextField("公共尾部", default='') # 全局HTML尾部
beian_code = models.CharField('备案号', max_length=2000, default='') # ICP备案号
analytics_code = models.TextField("网站统计代码", max_length=1000, default='') # 统计代码
show_gongan_code = models.BooleanField('是否显示公安备案号', default=False)
gongan_beiancode = models.TextField('公安备案号', max_length=2000, default='')
class Meta:
verbose_name = _('Website configuration')
verbose_name_plural = verbose_name
def clean(self):
"""验证确保只能有一个配置实例"""
if BlogSettings.objects.exclude(id=self.id).count():
raise ValidationError(_('There can only be one configuration'))
def save(self, *args, **kwargs):
"""保存时清空缓存,确保配置及时生效"""
super().save(*args, **kwargs)
from djangoblog.utils import cache
cache.clear()
Loading…
Cancel
Save