|
|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
|
|
|
|
|
# 这个文件里的是博客相关的数据模型,定义了博客系统中所有的数据表结构
|
|
|
|
|
#flj 这个文件里的是博客相关的数据模型,定义了博客系统中所有的数据表结构
|
|
|
|
|
import logging
|
|
|
|
|
import re
|
|
|
|
|
from abc import abstractmethod
|
|
|
|
|
@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 友情链接的展示类型选择,用于控制链接在哪些页面显示
|
|
|
|
|
#zxm 友情链接的展示类型选择,用于控制链接在哪些页面显示
|
|
|
|
|
class LinkShowType(models.TextChoices):
|
|
|
|
|
I = ('i', _('index')) # 只在首页显示
|
|
|
|
|
L = ('l', _('list')) # 只在列表页显示
|
|
|
|
|
@ -29,22 +29,22 @@ class LinkShowType(models.TextChoices):
|
|
|
|
|
S = ('s', _('slide')) # 以轮播形式显示
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 所有模型的基类,包含通用字段,避免重复代码
|
|
|
|
|
#fkc 所有模型的基类,包含通用字段,避免重复代码
|
|
|
|
|
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) # 最后修改时间
|
|
|
|
|
id = models.AutoField(primary_key=True) #fkc 主键,自动递增
|
|
|
|
|
creation_time = models.DateTimeField(_('creation time'), default=now) #fkc 创建时间
|
|
|
|
|
last_modify_time = models.DateTimeField(_('modify time'), default=now) #fkc 最后修改时间
|
|
|
|
|
|
|
|
|
|
# 重写保存方法,自动处理slug字段(用于生成友好的URL)
|
|
|
|
|
#fkc 重写保存方法,自动处理slug字段(用于生成友好的URL)
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
|
# 如果是更新文章浏览量,直接更新数据库,避免触发其他逻辑
|
|
|
|
|
#fkc 如果是更新文章浏览量,直接更新数据库,避免触发其他逻辑
|
|
|
|
|
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字段,自动从标题或名称生成slug
|
|
|
|
|
#fkc 如果有slug字段,自动从标题或名称生成slug
|
|
|
|
|
if 'slug' in self.__dict__:
|
|
|
|
|
slug = getattr(
|
|
|
|
|
self, 'title') if 'title' in self.__dict__ else getattr(
|
|
|
|
|
@ -53,358 +53,345 @@ class BaseModel(models.Model):
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def get_full_url(self):
|
|
|
|
|
# 获取完整的URL地址,包含域名
|
|
|
|
|
#fkc 获取完整的URL地址,包含域名
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
url = "https://{site}{path}".format(site=site,
|
|
|
|
|
path=self.get_absolute_url())
|
|
|
|
|
return url
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
abstract = True # 这是一个抽象基类,不会创建数据库表
|
|
|
|
|
abstract = True #fkc 这是一个抽象基类,不会创建数据库表
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
|
# 子类必须实现这个方法,返回对象的URL
|
|
|
|
|
#fkc 子类必须实现这个方法,返回对象的URL
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#cll 文章模型,博客的核心内容
|
|
|
|
|
class Article(BaseModel):
|
|
|
|
|
"""文章模型,博客的核心内容"""
|
|
|
|
|
# 文章状态选择:草稿或已发布
|
|
|
|
|
#cll 文章状态选择:草稿或已发布
|
|
|
|
|
STATUS_CHOICES = (
|
|
|
|
|
('d', _('Draft')), # 草稿
|
|
|
|
|
('p', _('Published')), # 已发布
|
|
|
|
|
)
|
|
|
|
|
# 评论状态选择:开放或关闭
|
|
|
|
|
#cll 评论状态选择:开放或关闭
|
|
|
|
|
COMMENT_STATUS = (
|
|
|
|
|
('o', _('Open')), # 开放评论
|
|
|
|
|
('c', _('Close')), # 关闭评论
|
|
|
|
|
)
|
|
|
|
|
# 内容类型选择:文章或页面
|
|
|
|
|
#cll 内容类型选择:文章或页面
|
|
|
|
|
TYPE = (
|
|
|
|
|
('a', _('Article')), # 普通文章
|
|
|
|
|
('p', _('Page')), # 静态页面
|
|
|
|
|
)
|
|
|
|
|
title = models.CharField(_('title'), max_length=200, unique=True) # 文章标题
|
|
|
|
|
body = MDTextField(_('body')) # 文章正文,支持Markdown格式
|
|
|
|
|
title = models.CharField(_('title'), max_length=200, unique=True) #cll 文章标题
|
|
|
|
|
body = MDTextField(_('body')) #cll 文章正文,支持Markdown格式
|
|
|
|
|
pub_time = models.DateTimeField(
|
|
|
|
|
_('publish time'), blank=False, null=False, default=now) # 发布时间
|
|
|
|
|
_('publish time'), blank=False, null=False, default=now) #cll 发布时间
|
|
|
|
|
status = models.CharField(
|
|
|
|
|
_('status'),
|
|
|
|
|
max_length=1,
|
|
|
|
|
choices=STATUS_CHOICES,
|
|
|
|
|
default='p') # 文章状态
|
|
|
|
|
default='p') #cll 文章状态
|
|
|
|
|
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) # 浏览次数
|
|
|
|
|
default='o') #cll 评论状态
|
|
|
|
|
type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a') #cll 内容类型
|
|
|
|
|
views = models.PositiveIntegerField(_('views'), default=0) #cll 浏览次数
|
|
|
|
|
author = models.ForeignKey(
|
|
|
|
|
settings.AUTH_USER_MODEL,
|
|
|
|
|
verbose_name=_('author'),
|
|
|
|
|
blank=False,
|
|
|
|
|
null=False,
|
|
|
|
|
on_delete=models.CASCADE) # 作者,关联用户表
|
|
|
|
|
on_delete=models.CASCADE) #cll 作者,关联用户表
|
|
|
|
|
article_order = models.IntegerField(
|
|
|
|
|
_('order'), blank=False, null=False, default=0) # 文章排序
|
|
|
|
|
show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) # 是否显示目录
|
|
|
|
|
_('order'), blank=False, null=False, default=0) #cll 文章排序
|
|
|
|
|
show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) #cll 是否显示目录
|
|
|
|
|
category = models.ForeignKey(
|
|
|
|
|
'Category',
|
|
|
|
|
verbose_name=_('category'),
|
|
|
|
|
on_delete=models.CASCADE,
|
|
|
|
|
blank=False,
|
|
|
|
|
null=False) # 分类,关联分类表
|
|
|
|
|
tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) # 标签,多对多关系
|
|
|
|
|
null=False) #cll 分类,关联分类表
|
|
|
|
|
tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) #cll 标签,多对多关系
|
|
|
|
|
|
|
|
|
|
#cll 将文章内容转换为字符串
|
|
|
|
|
def body_to_string(self):
|
|
|
|
|
# 将文章正文转换为字符串
|
|
|
|
|
return self.body
|
|
|
|
|
|
|
|
|
|
#cll 返回文章标题作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回文章标题作为字符串表示
|
|
|
|
|
return self.title
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['-article_order', '-pub_time'] # 按排序字段和发布时间倒序排列
|
|
|
|
|
verbose_name = _('article') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
get_latest_by = 'id' # 获取最新记录的依据
|
|
|
|
|
ordering = ['-article_order', '-pub_time'] #cll 按排序字段和发布时间倒序排列
|
|
|
|
|
verbose_name = _('article') #cll 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #cll 复数形式
|
|
|
|
|
get_latest_by = 'id' #cll 获取最新记录的依据
|
|
|
|
|
|
|
|
|
|
#cll 获取文章的URL
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
|
# 获取文章的URL地址,包含年月日信息
|
|
|
|
|
return reverse('blog:detailbyid', kwargs={
|
|
|
|
|
'article_id': self.id,
|
|
|
|
|
'year': self.creation_time.year,
|
|
|
|
|
'month': self.creation_time.month,
|
|
|
|
|
'day': self.creation_time.day
|
|
|
|
|
})
|
|
|
|
|
if self.type == 'a':
|
|
|
|
|
return reverse('blog:detail', kwargs={'article_id': self.id, 'slug': self.slug})
|
|
|
|
|
elif self.type == 'p':
|
|
|
|
|
return reverse('blog:page', kwargs={'article_id': self.id, 'slug': self.slug})
|
|
|
|
|
|
|
|
|
|
#cll 获取分类树,缓存10小时
|
|
|
|
|
@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))
|
|
|
|
|
category = self.category
|
|
|
|
|
names = [category.name]
|
|
|
|
|
while category.parent_category:
|
|
|
|
|
category = category.parent_category
|
|
|
|
|
names.append(category.name)
|
|
|
|
|
return names
|
|
|
|
|
|
|
|
|
|
#cll 保存文章,更新修改时间
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
|
# 保存文章
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
self.last_modify_time = now()
|
|
|
|
|
return super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
#cll 增加文章浏览次数
|
|
|
|
|
def viewed(self):
|
|
|
|
|
# 增加文章浏览次数
|
|
|
|
|
self.views += 1
|
|
|
|
|
self.save(update_fields=['views'])
|
|
|
|
|
|
|
|
|
|
#cll 获取文章评论列表
|
|
|
|
|
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)
|
|
|
|
|
logger.info('set article comments:{id}'.format(id=self.id))
|
|
|
|
|
return comments
|
|
|
|
|
comments = self.comment_set.filter(is_enable=True).order_by('-id')
|
|
|
|
|
return comments
|
|
|
|
|
|
|
|
|
|
#cll 获取文章在管理后台的URL
|
|
|
|
|
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,))
|
|
|
|
|
return reverse('admin:%s_%s_change' % info, args=(self.id,))
|
|
|
|
|
|
|
|
|
|
#cll 获取下一篇文章,缓存100分钟
|
|
|
|
|
@cache_decorator(expiration=60 * 100) # 缓存100分钟
|
|
|
|
|
def next_article(self):
|
|
|
|
|
# 获取下一篇已发布的文章
|
|
|
|
|
return Article.objects.filter(
|
|
|
|
|
id__gt=self.id, status='p').order_by('id').first()
|
|
|
|
|
return Article.objects.filter(id__gt=self.id, status='p').order_by('id').first()
|
|
|
|
|
|
|
|
|
|
#cll 获取上一篇文章,缓存100分钟
|
|
|
|
|
@cache_decorator(expiration=60 * 100) # 缓存100分钟
|
|
|
|
|
def prev_article(self):
|
|
|
|
|
# 获取上一篇已发布的文章
|
|
|
|
|
return Article.objects.filter(id__lt=self.id, status='p').first()
|
|
|
|
|
return Article.objects.filter(id__lt=self.id, status='p').order_by('-id').first()
|
|
|
|
|
|
|
|
|
|
#cll 获取文章中的第一张图片URL
|
|
|
|
|
def get_first_image_url(self):
|
|
|
|
|
# 从文章正文中提取第一张图片的URL
|
|
|
|
|
match = re.search(r'!\[.*?\]\((.+?)\)', self.body)
|
|
|
|
|
if match:
|
|
|
|
|
return match.group(1)
|
|
|
|
|
return ""
|
|
|
|
|
pattern = re.compile(r'<img.*?src=["|\'](.*?)["|\']', re.S)
|
|
|
|
|
result = pattern.search(self.body)
|
|
|
|
|
if result:
|
|
|
|
|
return result.group(1)
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#xy 分类模型
|
|
|
|
|
class Category(BaseModel):
|
|
|
|
|
"""文章分类模型,支持层级结构"""
|
|
|
|
|
name = models.CharField(_('category name'), max_length=30, unique=True) # 分类名称
|
|
|
|
|
name = models.CharField(_('category name'), max_length=30, unique=True) #xy 分类名称
|
|
|
|
|
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) # URL友好的标识符
|
|
|
|
|
index = models.IntegerField(default=0, verbose_name=_('index')) # 排序索引
|
|
|
|
|
on_delete=models.CASCADE) #xy 父分类,支持多级分类
|
|
|
|
|
slug = models.SlugField(default='no-slug', max_length=60, blank=True) #xy URL友好的标识符
|
|
|
|
|
index = models.IntegerField(default=0, verbose_name=_('index')) #xy 排序索引
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['-index'] # 按索引倒序排列
|
|
|
|
|
verbose_name = _('category') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
ordering = ['-index'] #xy 按索引倒序排列
|
|
|
|
|
verbose_name = _('category') #xy 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #xy 复数形式
|
|
|
|
|
|
|
|
|
|
#xy 获取分类的URL
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
|
# 获取分类的URL地址
|
|
|
|
|
return reverse(
|
|
|
|
|
'blog:category_detail', kwargs={
|
|
|
|
|
'category_name': self.slug})
|
|
|
|
|
return reverse('blog:category_detail', kwargs={'category_name': self.slug})
|
|
|
|
|
|
|
|
|
|
#xy 返回分类名称作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回分类名称作为字符串表示
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
#xy 获取分类树,缓存10小时
|
|
|
|
|
@cache_decorator(60 * 60 * 10) # 缓存10小时
|
|
|
|
|
def get_category_tree(self):
|
|
|
|
|
# 递归获取分类的父级分类树(从当前分类到根分类)
|
|
|
|
|
categorys = []
|
|
|
|
|
|
|
|
|
|
def parse(category):
|
|
|
|
|
categorys.append(category)
|
|
|
|
|
if category.parent_category:
|
|
|
|
|
parse(category.parent_category)
|
|
|
|
|
|
|
|
|
|
parse(self)
|
|
|
|
|
return categorys
|
|
|
|
|
names = [self.name]
|
|
|
|
|
category = self.parent_category
|
|
|
|
|
while category:
|
|
|
|
|
names.append(category.name)
|
|
|
|
|
category = category.parent_category
|
|
|
|
|
return names
|
|
|
|
|
|
|
|
|
|
#xy 获取子分类列表,缓存10小时
|
|
|
|
|
@cache_decorator(60 * 60 * 10) # 缓存10小时
|
|
|
|
|
def get_sub_categorys(self):
|
|
|
|
|
# 递归获取当前分类的所有子分类
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
categories = []
|
|
|
|
|
all_categories = Category.objects.all()
|
|
|
|
|
for category in all_categories:
|
|
|
|
|
names = [category.name]
|
|
|
|
|
c = category.parent_category
|
|
|
|
|
while c:
|
|
|
|
|
names.append(c.name)
|
|
|
|
|
c = c.parent_category
|
|
|
|
|
if self.name in names:
|
|
|
|
|
categories.append(category)
|
|
|
|
|
return categories
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhj 标签模型
|
|
|
|
|
class Tag(BaseModel):
|
|
|
|
|
"""文章标签模型"""
|
|
|
|
|
name = models.CharField(_('tag name'), max_length=30, unique=True) # 标签名称
|
|
|
|
|
slug = models.SlugField(default='no-slug', max_length=60, blank=True) # URL友好的标识符
|
|
|
|
|
name = models.CharField(_('tag name'), max_length=30, unique=True) #zhj 标签名称
|
|
|
|
|
slug = models.SlugField(default='no-slug', max_length=60, blank=True) #zhj URL友好的标识符
|
|
|
|
|
|
|
|
|
|
#zhj 返回标签名称作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回标签名称作为字符串表示
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
#zhj 获取标签的URL
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
|
# 获取标签的URL地址
|
|
|
|
|
return reverse('blog:tag_detail', kwargs={'tag_name': self.slug})
|
|
|
|
|
|
|
|
|
|
#zhj 获取标签下的文章数量,缓存10小时
|
|
|
|
|
@cache_decorator(60 * 60 * 10) # 缓存10小时
|
|
|
|
|
def get_article_count(self):
|
|
|
|
|
# 获取使用该标签的文章数量
|
|
|
|
|
return Article.objects.filter(tags__name=self.name).distinct().count()
|
|
|
|
|
return Article.objects.filter(tags=self).count()
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['name'] # 按名称排序
|
|
|
|
|
verbose_name = _('tag') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
ordering = ['name'] #zhj 按名称排序
|
|
|
|
|
verbose_name = _('tag') #zhj 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #zhj 复数形式
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#flj 友情链接模型
|
|
|
|
|
class Links(models.Model):
|
|
|
|
|
"""友情链接模型"""
|
|
|
|
|
|
|
|
|
|
name = models.CharField(_('link name'), max_length=30, unique=True) # 链接名称
|
|
|
|
|
link = models.URLField(_('link')) # 链接地址
|
|
|
|
|
sequence = models.IntegerField(_('order'), unique=True) # 排序序号
|
|
|
|
|
name = models.CharField(_('link name'), max_length=30, unique=True) #flj 链接名称
|
|
|
|
|
link = models.URLField(_('link')) #flj 链接地址
|
|
|
|
|
sequence = models.IntegerField(_('order'), unique=True) #flj 排序序号
|
|
|
|
|
is_enable = models.BooleanField(
|
|
|
|
|
_('is show'), default=True, blank=False, null=False) # 是否启用
|
|
|
|
|
_('is show'), default=True, blank=False, null=False) #flj 是否启用
|
|
|
|
|
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) # 修改时间
|
|
|
|
|
default=LinkShowType.I) #flj 显示类型
|
|
|
|
|
creation_time = models.DateTimeField(_('creation time'), default=now) #flj 创建时间
|
|
|
|
|
last_mod_time = models.DateTimeField(_('modify time'), default=now) #flj 修改时间
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['sequence'] # 按序号排序
|
|
|
|
|
verbose_name = _('link') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
ordering = ['sequence'] #flj 按序号排序
|
|
|
|
|
verbose_name = _('link') #flj 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #flj 复数形式
|
|
|
|
|
|
|
|
|
|
#flj 返回链接名称作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回链接名称作为字符串表示
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zxm 侧边栏模型
|
|
|
|
|
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) # 修改时间
|
|
|
|
|
name = models.CharField(_('title'), max_length=100) #zxm 侧边栏标题
|
|
|
|
|
content = models.TextField(_('content')) #zxm 侧边栏内容(支持HTML)
|
|
|
|
|
sequence = models.IntegerField(_('order'), unique=True) #zxm 排序序号
|
|
|
|
|
is_enable = models.BooleanField(_('is enable'), default=True) #zxm 是否启用
|
|
|
|
|
creation_time = models.DateTimeField(_('creation time'), default=now) #zxm 创建时间
|
|
|
|
|
last_mod_time = models.DateTimeField(_('modify time'), default=now) #zxm 修改时间
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['sequence'] # 按序号排序
|
|
|
|
|
verbose_name = _('sidebar') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
ordering = ['sequence'] #zxm 按序号排序
|
|
|
|
|
verbose_name = _('sidebar') #zxm 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #zxm 复数形式
|
|
|
|
|
|
|
|
|
|
#zxm 返回侧边栏标题作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回侧边栏标题作为字符串表示
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#fkc 网站设置模型
|
|
|
|
|
class BlogSettings(models.Model):
|
|
|
|
|
"""博客配置模型,存储网站的各种设置"""
|
|
|
|
|
# 网站基本信息
|
|
|
|
|
site_name = models.CharField(
|
|
|
|
|
_('site name'),
|
|
|
|
|
max_length=200,
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
default='') # 网站名称
|
|
|
|
|
default='') #fkc 网站名称
|
|
|
|
|
site_description = models.TextField(
|
|
|
|
|
_('site description'),
|
|
|
|
|
max_length=1000,
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
default='') # 网站描述
|
|
|
|
|
default='') #fkc 网站描述
|
|
|
|
|
site_seo_description = models.TextField(
|
|
|
|
|
_('site seo description'), max_length=1000, null=False, blank=False, default='') # SEO描述
|
|
|
|
|
_('site seo description'), max_length=1000, null=False, blank=False, default='') #fkc SEO描述
|
|
|
|
|
site_keywords = models.TextField(
|
|
|
|
|
_('site keywords'),
|
|
|
|
|
max_length=1000,
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
default='') # 网站关键词
|
|
|
|
|
default='') #fkc 网站关键词
|
|
|
|
|
|
|
|
|
|
# 显示设置
|
|
|
|
|
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) # 文章页评论数量
|
|
|
|
|
article_sub_length = models.IntegerField(_('article sub length'), default=300) #fkc 文章摘要长度
|
|
|
|
|
sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) #fkc 侧边栏文章数量
|
|
|
|
|
sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) #fkc 侧边栏评论数量
|
|
|
|
|
article_comment_count = models.IntegerField(_('article comment count'), default=5) #fkc 文章页评论数量
|
|
|
|
|
|
|
|
|
|
# 广告设置
|
|
|
|
|
show_google_adsense = models.BooleanField(_('show adsense'), default=False) # 是否显示广告
|
|
|
|
|
show_google_adsense = models.BooleanField(_('show adsense'), default=False) #fkc 是否显示广告
|
|
|
|
|
google_adsense_codes = models.TextField(
|
|
|
|
|
_('adsense code'), max_length=2000, null=True, blank=True, default='') # 广告代码
|
|
|
|
|
_('adsense code'), max_length=2000, null=True, blank=True, default='') #fkc 广告代码
|
|
|
|
|
|
|
|
|
|
# 评论设置
|
|
|
|
|
open_site_comment = models.BooleanField(_('open site comment'), default=True) # 是否开放评论
|
|
|
|
|
open_site_comment = models.BooleanField(_('open site comment'), default=True) #fkc 是否开放评论
|
|
|
|
|
comment_need_review = models.BooleanField(
|
|
|
|
|
'评论是否需要审核', default=False, null=False) # 评论是否需要审核
|
|
|
|
|
'评论是否需要审核', default=False, null=False) #fkc 评论是否需要审核
|
|
|
|
|
|
|
|
|
|
# 页面设置
|
|
|
|
|
global_header = models.TextField("公共头部", null=True, blank=True, default='') # 公共头部HTML
|
|
|
|
|
global_footer = models.TextField("公共尾部", null=True, blank=True, default='') # 公共尾部HTML
|
|
|
|
|
global_header = models.TextField("公共头部", null=True, blank=True, default='') #fkc 公共头部HTML
|
|
|
|
|
global_footer = models.TextField("公共尾部", null=True, blank=True, default='') #fkc 公共尾部HTML
|
|
|
|
|
|
|
|
|
|
# 备案信息
|
|
|
|
|
beian_code = models.CharField(
|
|
|
|
|
'备案号',
|
|
|
|
|
max_length=2000,
|
|
|
|
|
null=True,
|
|
|
|
|
blank=True,
|
|
|
|
|
default='') # ICP备案号
|
|
|
|
|
default='') #fkc ICP备案号
|
|
|
|
|
show_gongan_code = models.BooleanField(
|
|
|
|
|
'是否显示公安备案号', default=False, null=False) # 是否显示公安备案号
|
|
|
|
|
'是否显示公安备案号', default=False, null=False) #fkc 是否显示公安备案号
|
|
|
|
|
gongan_beiancode = models.TextField(
|
|
|
|
|
'公安备案号',
|
|
|
|
|
max_length=2000,
|
|
|
|
|
null=True,
|
|
|
|
|
blank=True,
|
|
|
|
|
default='') # 公安备案号
|
|
|
|
|
default='') #fkc 公安备案号
|
|
|
|
|
|
|
|
|
|
# 统计代码
|
|
|
|
|
analytics_code = models.TextField(
|
|
|
|
|
"网站统计代码",
|
|
|
|
|
max_length=1000,
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
default='') # 网站统计代码(如百度统计、Google Analytics等)
|
|
|
|
|
default='') #fkc 网站统计代码(如百度统计、Google Analytics等)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
verbose_name = _('Website configuration') # 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name # 复数形式
|
|
|
|
|
verbose_name = _('Website configuration') #fkc 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #fkc 复数形式
|
|
|
|
|
|
|
|
|
|
#fkc 返回网站名称作为对象的字符串表示
|
|
|
|
|
def __str__(self):
|
|
|
|
|
# 返回网站名称作为字符串表示
|
|
|
|
|
return self.site_name
|
|
|
|
|
|
|
|
|
|
#fkc 验证模型数据
|
|
|
|
|
def clean(self):
|
|
|
|
|
# 确保只能有一个配置实例
|
|
|
|
|
if BlogSettings.objects.exclude(id=self.id).count():
|
|
|
|
|
raise ValidationError(_('There can only be one configuration'))
|
|
|
|
|
if BlogSettings.objects.exclude(id=self.id).exists():
|
|
|
|
|
raise ValidationError(_('You can only create one config!'))
|
|
|
|
|
|
|
|
|
|
#fkc 保存设置,更新修改时间
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
|
# 保存配置后清除缓存,确保设置立即生效
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
self.last_mod_time = now()
|
|
|
|
|
from djangoblog.utils import cache
|
|
|
|
|
cache.clear()
|
|
|
|
|
return super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
verbose_name = _('Website configuration') #fkc 在管理后台显示的名称
|
|
|
|
|
verbose_name_plural = verbose_name #fkc 复数形式
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return self.site_name
|
|
|
|
|
|