|
|
|
|
@ -16,27 +16,28 @@ from djangoblog.utils import get_current_site
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 链接显示类型选择类 - 定义链接在不同页面的显示方式
|
|
|
|
|
class LinkShowType(models.TextChoices):
|
|
|
|
|
I = ('i', _('index'))
|
|
|
|
|
L = ('l', _('list'))
|
|
|
|
|
P = ('p', _('post'))
|
|
|
|
|
A = ('a', _('all'))
|
|
|
|
|
S = ('s', _('slide'))
|
|
|
|
|
|
|
|
|
|
I = ('i', _('index'))#zhq: 首页显示
|
|
|
|
|
L = ('l', _('list'))#zhq: 列表页显示
|
|
|
|
|
P = ('p', _('post'))#zhq: 文章页显示
|
|
|
|
|
A = ('a', _('all'))#zhq: 所有页面显示
|
|
|
|
|
S = ('s', _('slide'))#zhq: 幻灯片显示
|
|
|
|
|
|
|
|
|
|
#zhq: 基础模型抽象类 - 所有模型的基类,包含公共字段和方法
|
|
|
|
|
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):
|
|
|
|
|
def save(self, *args, **kwargs):#zhq: 特殊处理文章浏览量的更新,避免触发其他字段的保存逻辑
|
|
|
|
|
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:
|
|
|
|
|
# zhq: 自动生成slug字段,用于SEO友好的URL
|
|
|
|
|
if 'slug' in self.__dict__:
|
|
|
|
|
slug = getattr(
|
|
|
|
|
self, 'title') if 'title' in self.__dict__ else getattr(
|
|
|
|
|
@ -45,6 +46,7 @@ class BaseModel(models.Model):
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def get_full_url(self):
|
|
|
|
|
# zhq: 获取对象的完整URL地址
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
url = "https://{site}{path}".format(site=site,
|
|
|
|
|
path=self.get_absolute_url())
|
|
|
|
|
@ -55,9 +57,10 @@ class BaseModel(models.Model):
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
|
# zhq: 抽象方法,子类必须实现获取绝对URL的方法
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 文章模型 - 博客的核心内容模型
|
|
|
|
|
class Article(BaseModel):
|
|
|
|
|
"""文章"""
|
|
|
|
|
STATUS_CHOICES = (
|
|
|
|
|
@ -112,7 +115,7 @@ class Article(BaseModel):
|
|
|
|
|
return self.title
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['-article_order', '-pub_time']
|
|
|
|
|
ordering = ['-article_order', '-pub_time']#zhq: 按排序和发布时间降序排列
|
|
|
|
|
verbose_name = _('article')
|
|
|
|
|
verbose_name_plural = verbose_name
|
|
|
|
|
get_latest_by = 'id'
|
|
|
|
|
@ -127,6 +130,7 @@ class Article(BaseModel):
|
|
|
|
|
|
|
|
|
|
@cache_decorator(60 * 60 * 10)
|
|
|
|
|
def get_category_tree(self):
|
|
|
|
|
# zhq: 获取文章所属分类的完整树形结构
|
|
|
|
|
tree = self.category.get_category_tree()
|
|
|
|
|
names = list(map(lambda c: (c.name, c.get_absolute_url()), tree))
|
|
|
|
|
|
|
|
|
|
@ -136,10 +140,12 @@ class Article(BaseModel):
|
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
def viewed(self):
|
|
|
|
|
# zhq: 增加文章浏览量
|
|
|
|
|
self.views += 1
|
|
|
|
|
self.save(update_fields=['views'])
|
|
|
|
|
|
|
|
|
|
def comment_list(self):
|
|
|
|
|
# zhq: 获取文章的评论列表,带缓存功能
|
|
|
|
|
cache_key = 'article_comments_{id}'.format(id=self.id)
|
|
|
|
|
value = cache.get(cache_key)
|
|
|
|
|
if value:
|
|
|
|
|
@ -152,6 +158,7 @@ class Article(BaseModel):
|
|
|
|
|
return comments
|
|
|
|
|
|
|
|
|
|
def get_admin_url(self):
|
|
|
|
|
# zhq: 获取文章在Admin后台的编辑链接
|
|
|
|
|
info = (self._meta.app_label, self._meta.model_name)
|
|
|
|
|
return reverse('admin:%s_%s_change' % info, args=(self.pk,))
|
|
|
|
|
|
|
|
|
|
@ -176,7 +183,7 @@ class Article(BaseModel):
|
|
|
|
|
return match.group(1)
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 分类模型 - 支持多级分类结构
|
|
|
|
|
class Category(BaseModel):
|
|
|
|
|
"""文章分类"""
|
|
|
|
|
name = models.CharField(_('category name'), max_length=30, unique=True)
|
|
|
|
|
@ -185,7 +192,7 @@ class Category(BaseModel):
|
|
|
|
|
verbose_name=_('parent category'),
|
|
|
|
|
blank=True,
|
|
|
|
|
null=True,
|
|
|
|
|
on_delete=models.CASCADE)
|
|
|
|
|
on_delete=models.CASCADE) #zhq: 自关联,支持多级分类
|
|
|
|
|
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
|
|
|
|
|
index = models.IntegerField(default=0, verbose_name=_('index'))
|
|
|
|
|
|
|
|
|
|
@ -239,7 +246,7 @@ class Category(BaseModel):
|
|
|
|
|
parse(self)
|
|
|
|
|
return categorys
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 标签模型 - 简单的标签管理
|
|
|
|
|
class Tag(BaseModel):
|
|
|
|
|
"""文章标签"""
|
|
|
|
|
name = models.CharField(_('tag name'), max_length=30, unique=True)
|
|
|
|
|
@ -253,6 +260,7 @@ class Tag(BaseModel):
|
|
|
|
|
|
|
|
|
|
@cache_decorator(60 * 60 * 10)
|
|
|
|
|
def get_article_count(self):
|
|
|
|
|
# zhq: 获取该标签下的文章数量
|
|
|
|
|
return Article.objects.filter(tags__name=self.name).distinct().count()
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
@ -260,32 +268,32 @@ class Tag(BaseModel):
|
|
|
|
|
verbose_name = _('tag')
|
|
|
|
|
verbose_name_plural = verbose_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 友情链接模型
|
|
|
|
|
class Links(models.Model):
|
|
|
|
|
"""友情链接"""
|
|
|
|
|
|
|
|
|
|
name = models.CharField(_('link name'), max_length=30, unique=True)
|
|
|
|
|
link = models.URLField(_('link'))
|
|
|
|
|
sequence = models.IntegerField(_('order'), unique=True)
|
|
|
|
|
sequence = models.IntegerField(_('order'), unique=True) #zhq: 链接显示顺序
|
|
|
|
|
is_enable = models.BooleanField(
|
|
|
|
|
_('is show'), default=True, blank=False, null=False)
|
|
|
|
|
show_type = models.CharField(
|
|
|
|
|
_('show type'),
|
|
|
|
|
max_length=1,
|
|
|
|
|
choices=LinkShowType.choices,
|
|
|
|
|
default=LinkShowType.I)
|
|
|
|
|
default=LinkShowType.I) #zhq: 链接显示类型
|
|
|
|
|
creation_time = models.DateTimeField(_('creation time'), default=now)
|
|
|
|
|
last_mod_time = models.DateTimeField(_('modify time'), default=now)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
ordering = ['sequence']
|
|
|
|
|
ordering = ['sequence'] #zhq: 按顺序排列
|
|
|
|
|
verbose_name = _('link')
|
|
|
|
|
verbose_name_plural = verbose_name
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 侧边栏模型 - 支持自定义HTML内容
|
|
|
|
|
class SideBar(models.Model):
|
|
|
|
|
"""侧边栏,可以展示一些html内容"""
|
|
|
|
|
name = models.CharField(_('title'), max_length=100)
|
|
|
|
|
@ -303,7 +311,7 @@ class SideBar(models.Model):
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zhq: 博客设置模型 - 单例模式,存储全局配置
|
|
|
|
|
class BlogSettings(models.Model):
|
|
|
|
|
"""blog的配置"""
|
|
|
|
|
site_name = models.CharField(
|
|
|
|
|
@ -326,11 +334,11 @@ class BlogSettings(models.Model):
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
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)
|
|
|
|
|
article_sub_length = models.IntegerField(_('article sub length'), default=300) #zhq: 文章摘要长度
|
|
|
|
|
sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) #zhq: 侧边栏文章数量
|
|
|
|
|
sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) #zhq: 侧边栏评论数量
|
|
|
|
|
article_comment_count = models.IntegerField(_('article comment count'), default=5) #zhq: 文章页评论数量
|
|
|
|
|
show_google_adsense = models.BooleanField(_('show adsense'), default=False) #zhq:是否显示Google广告
|
|
|
|
|
google_adsense_codes = models.TextField(
|
|
|
|
|
_('adsense code'), max_length=2000, null=True, blank=True, default='')
|
|
|
|
|
open_site_comment = models.BooleanField(_('open site comment'), default=True)
|
|
|
|
|
@ -341,13 +349,13 @@ class BlogSettings(models.Model):
|
|
|
|
|
max_length=2000,
|
|
|
|
|
null=True,
|
|
|
|
|
blank=True,
|
|
|
|
|
default='')
|
|
|
|
|
default='') #zhq: ICP备案号
|
|
|
|
|
analytics_code = models.TextField(
|
|
|
|
|
"网站统计代码",
|
|
|
|
|
max_length=1000,
|
|
|
|
|
null=False,
|
|
|
|
|
blank=False,
|
|
|
|
|
default='')
|
|
|
|
|
default='') #zhq: 网站统计代码
|
|
|
|
|
show_gongan_code = models.BooleanField(
|
|
|
|
|
'是否显示公安备案号', default=False, null=False)
|
|
|
|
|
gongan_beiancode = models.TextField(
|
|
|
|
|
@ -357,7 +365,7 @@ class BlogSettings(models.Model):
|
|
|
|
|
blank=True,
|
|
|
|
|
default='')
|
|
|
|
|
comment_need_review = models.BooleanField(
|
|
|
|
|
'评论是否需要审核', default=False, null=False)
|
|
|
|
|
'评论是否需要审核', default=False, null=False) #zhq: 评论审核开关
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
verbose_name = _('Website configuration')
|
|
|
|
|
@ -367,6 +375,7 @@ class BlogSettings(models.Model):
|
|
|
|
|
return self.site_name
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
# zhq: 确保配置表只有一条记录(单例模式)
|
|
|
|
|
if BlogSettings.objects.exclude(id=self.id).count():
|
|
|
|
|
raise ValidationError(_('There can only be one configuration'))
|
|
|
|
|
|
|
|
|
|
|