@ -3,10 +3,12 @@
import logging
import logging
import re
import re
from abc import abstractmethod
from abc import abstractmethod
from django . utils . functional import cached_property
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 . db . models import Count
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 _
@ -30,10 +32,17 @@ class LinkShowType(models.TextChoices):
#fkc 所有模型的基类,包含通用字段,避免重复代码
#fkc 所有模型的基类,包含通用字段,避免重复代码
# 定义缓存时间常量,避免重复使用字面量
CACHE_10_HOURS = 60 * 60 * 10
# 字段名称常量
CREATION_TIME_LABEL = ' creation time '
MODIFY_TIME_LABEL = ' modify time '
CACHE_100_MINUTES = 60 * 100
class BaseModel ( models . Model ) :
class BaseModel ( models . Model ) :
id = models . AutoField ( primary_key = True ) #fkc 主键,自动递增
id = models . AutoField ( primary_key = True ) #fkc 主键,自动递增
creation_time = models . DateTimeField ( _ ( ' creation time ' ) , default = now ) #fkc 创建时间
creation_time = models . DateTimeField ( _ ( CREATION_TIME_LABEL ) , default = now ) #fkc 创建时间
last_modify_time = models . DateTimeField ( _ ( ' modify time ' ) , default = now ) #fkc 最后修改时间
last_modify_time = models . DateTimeField ( _ ( MODIFY_TIME_LABEL ) , default = now ) #fkc 最后修改时间
#fkc 重写保存方法, 自动处理slug字段( 用于生成友好的URL)
#fkc 重写保存方法, 自动处理slug字段( 用于生成友好的URL)
def save ( self , * args , * * kwargs ) :
def save ( self , * args , * * kwargs ) :
@ -86,6 +95,7 @@ class Article(BaseModel):
( ' p ' , _ ( ' Page ' ) ) , # 静态页面
( ' p ' , _ ( ' Page ' ) ) , # 静态页面
)
)
title = models . CharField ( _ ( ' title ' ) , max_length = 200 , unique = True ) #cll 文章标题
title = models . CharField ( _ ( ' title ' ) , max_length = 200 , unique = True ) #cll 文章标题
slug = models . SlugField ( _ ( ' slug ' ) , max_length = 200 , blank = True , null = True ) #cll URL友好的标识符
body = MDTextField ( _ ( ' body ' ) ) #cll 文章正文, 支持Markdown格式
body = MDTextField ( _ ( ' body ' ) ) #cll 文章正文, 支持Markdown格式
pub_time = models . DateTimeField (
pub_time = models . DateTimeField (
_ ( ' publish time ' ) , blank = False , null = False , default = now ) #cll 发布时间
_ ( ' publish time ' ) , blank = False , null = False , default = now ) #cll 发布时间
@ -99,7 +109,7 @@ class Article(BaseModel):
max_length = 1 ,
max_length = 1 ,
choices = COMMENT_STATUS ,
choices = COMMENT_STATUS ,
default = ' o ' ) #cll 评论状态
default = ' o ' ) #cll 评论状态
type = models . CharField ( _ ( ' type ' ) , max_length = 1 , choices = TYPE , default = ' a ' ) #cll 内容类型
article_ type = models . CharField ( _ ( ' type ' ) , max_length = 1 , choices = TYPE , default = ' a ' ) #cll 内容类型
views = models . PositiveIntegerField ( _ ( ' views ' ) , default = 0 ) #cll 浏览次数
views = models . PositiveIntegerField ( _ ( ' views ' ) , default = 0 ) #cll 浏览次数
author = models . ForeignKey (
author = models . ForeignKey (
settings . AUTH_USER_MODEL ,
settings . AUTH_USER_MODEL ,
@ -134,13 +144,13 @@ class Article(BaseModel):
#cll 获取文章的URL
#cll 获取文章的URL
def get_absolute_url ( self ) :
def get_absolute_url ( self ) :
if self . type == ' a ' :
if self . article_ type == ' a ' :
return reverse ( ' blog:detail ' , kwargs = { ' article_id ' : self . id , ' slug ' : self . slug } )
return reverse ( ' blog:detail ' , kwargs = { ' article_id ' : self . id , ' slug ' : self . slug } )
elif self . type == ' p ' :
elif self . article_ type == ' p ' :
return reverse ( ' blog:page ' , kwargs = { ' article_id ' : self . id , ' slug ' : self . slug } )
return reverse ( ' blog:page ' , kwargs = { ' article_id ' : self . id , ' slug ' : self . slug } )
#cll 获取分类树, 缓存10小时
#cll 获取分类树, 缓存10小时
@cache_decorator ( 60 * 60 * 10 ) # 缓存10小时
@cache_decorator ( CACHE_10_HOURS ) # 缓存10小时
def get_category_tree ( self ) :
def get_category_tree ( self ) :
category = self . category
category = self . category
names = [ category . name ]
names = [ category . name ]
@ -149,9 +159,18 @@ class Article(BaseModel):
names . append ( category . name )
names . append ( category . name )
return names
return names
#cll 保存文章,更新修改时间
#cll 保存文章,更新修改时间 并自动生成slug
def save ( self , * args , * * kwargs ) :
def save ( self , * args , * * kwargs ) :
self . last_modify_time = now ( )
self . last_modify_time = now ( )
# 自动生成slug
if not self . slug :
self . slug = slugify ( self . title )
# 确保slug唯一性
original_slug = self . slug
counter = 1
while Article . objects . filter ( slug = self . slug ) . exclude ( id = self . id ) . exists ( ) :
self . slug = f " { original_slug } - { counter } "
counter + = 1
return super ( ) . save ( * args , * * kwargs )
return super ( ) . save ( * args , * * kwargs )
#cll 增加文章浏览次数
#cll 增加文章浏览次数
@ -170,12 +189,12 @@ class Article(BaseModel):
return reverse ( ' admin: %s _ %s _change ' % info , args = ( self . id , ) )
return reverse ( ' admin: %s _ %s _change ' % info , args = ( self . id , ) )
#cll 获取下一篇文章, 缓存100分钟
#cll 获取下一篇文章, 缓存100分钟
@cache_decorator ( expiration = 60 * 100 ) # 缓存100分钟
@cache_decorator ( expiration = CACHE_100_MINUTES ) # 缓存100分钟
def next_article ( self ) :
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分钟
#cll 获取上一篇文章, 缓存100分钟
@cache_decorator ( expiration = 60 * 100 ) # 缓存100分钟
@cache_decorator ( expiration = CACHE_100_MINUTES ) # 缓存100分钟
def prev_article ( self ) :
def prev_article ( self ) :
return Article . objects . filter ( id__lt = self . id , status = ' p ' ) . order_by ( ' -id ' ) . first ( )
return Article . objects . filter ( id__lt = self . id , status = ' p ' ) . order_by ( ' -id ' ) . first ( )
@ -187,6 +206,14 @@ class Article(BaseModel):
return result . group ( 1 )
return result . group ( 1 )
else :
else :
return None
return None
@cached_property
def likes_count ( self ) :
return self . like_set . count ( )
@cached_property
def favorites_count ( self ) :
return self . favorite_set . count ( )
#xy 分类模型
#xy 分类模型
@ -215,7 +242,7 @@ class Category(BaseModel):
return self . name
return self . name
#xy 获取分类树, 缓存10小时
#xy 获取分类树, 缓存10小时
@cache_decorator ( 60 * 60 * 10 ) # 缓存10小时
@cache_decorator ( CACHE_10_HOURS ) # 缓存10小时
def get_category_tree ( self ) :
def get_category_tree ( self ) :
names = [ self . name ]
names = [ self . name ]
category = self . parent_category
category = self . parent_category
@ -225,7 +252,7 @@ class Category(BaseModel):
return names
return names
#xy 获取子分类列表, 缓存10小时
#xy 获取子分类列表, 缓存10小时
@cache_decorator ( 60 * 60 * 10 ) # 缓存10小时
@cache_decorator ( CACHE_10_HOURS ) # 缓存10小时
def get_sub_categorys ( self ) :
def get_sub_categorys ( self ) :
categories = [ ]
categories = [ ]
all_categories = Category . objects . all ( )
all_categories = Category . objects . all ( )
@ -254,7 +281,7 @@ class Tag(BaseModel):
return reverse ( ' blog:tag_detail ' , kwargs = { ' tag_name ' : self . slug } )
return reverse ( ' blog:tag_detail ' , kwargs = { ' tag_name ' : self . slug } )
#zhj 获取标签下的文章数量, 缓存10小时
#zhj 获取标签下的文章数量, 缓存10小时
@cache_decorator ( 60 * 60 * 10 ) # 缓存10小时
@cache_decorator ( CACHE_10_HOURS ) # 缓存10小时
def get_article_count ( self ) :
def get_article_count ( self ) :
return Article . objects . filter ( tags = self ) . count ( )
return Article . objects . filter ( tags = self ) . count ( )
@ -264,6 +291,9 @@ class Tag(BaseModel):
verbose_name_plural = verbose_name #zhj 复数形式
verbose_name_plural = verbose_name #zhj 复数形式
# 文章点赞和收藏模型将在文件末尾使用继承BaseModel的版本定义
#flj 友情链接模型
#flj 友情链接模型
class Links ( models . Model ) :
class Links ( models . Model ) :
name = models . CharField ( _ ( ' link name ' ) , max_length = 30 , unique = True ) #flj 链接名称
name = models . CharField ( _ ( ' link name ' ) , max_length = 30 , unique = True ) #flj 链接名称
@ -276,8 +306,8 @@ class Links(models.Model):
max_length = 1 ,
max_length = 1 ,
choices = LinkShowType . choices ,
choices = LinkShowType . choices ,
default = LinkShowType . I ) #flj 显示类型
default = LinkShowType . I ) #flj 显示类型
creation_time = models . DateTimeField ( _ ( ' creation time ' ) , default = now ) #flj 创建时间
creation_time = models . DateTimeField ( _ ( CREATION_TIME_LABEL ) , default = now ) #flj 创建时间
last_mod _time = models . DateTimeField ( _ ( ' modify time ' ) , default = now ) #flj 修改时间
last_mod ify _time = models . DateTimeField ( _ ( MODIFY_TIME_LABEL ) , default = now ) #flj 修改时间 ,与基类保持一致
class Meta :
class Meta :
ordering = [ ' sequence ' ] #flj 按序号排序
ordering = [ ' sequence ' ] #flj 按序号排序
@ -295,8 +325,8 @@ class SideBar(models.Model):
content = models . TextField ( _ ( ' content ' ) ) #zxm 侧边栏内容( 支持HTML)
content = models . TextField ( _ ( ' content ' ) ) #zxm 侧边栏内容( 支持HTML)
sequence = models . IntegerField ( _ ( ' order ' ) , unique = True ) #zxm 排序序号
sequence = models . IntegerField ( _ ( ' order ' ) , unique = True ) #zxm 排序序号
is_enable = models . BooleanField ( _ ( ' is enable ' ) , default = True ) #zxm 是否启用
is_enable = models . BooleanField ( _ ( ' is enable ' ) , default = True ) #zxm 是否启用
creation_time = models . DateTimeField ( _ ( ' creation time ' ) , default = now ) #zxm 创建时间
creation_time = models . DateTimeField ( _ ( CREATION_TIME_LABEL ) , default = now ) #zxm 创建时间
last_mod_time = models . DateTimeField ( _ ( ' modify time ' ) , default = now ) #zxm 修改时间
last_mod_time = models . DateTimeField ( _ ( MODIFY_TIME_LABEL ) , default = now ) #zxm 修改时间
class Meta :
class Meta :
ordering = [ ' sequence ' ] #zxm 按序号排序
ordering = [ ' sequence ' ] #zxm 按序号排序
@ -309,6 +339,45 @@ class SideBar(models.Model):
#fkc 网站设置模型
#fkc 网站设置模型
# 点赞模型
class Like ( BaseModel ) :
user = models . ForeignKey (
settings . AUTH_USER_MODEL ,
verbose_name = _ ( ' user ' ) ,
on_delete = models . CASCADE ) # 关联用户
article = models . ForeignKey (
Article ,
verbose_name = _ ( ' article ' ) ,
on_delete = models . CASCADE ) # 关联文章
class Meta :
unique_together = ( ' user ' , ' article ' ) # 确保一个用户只能对一篇文章点赞一次
verbose_name = _ ( ' like ' )
verbose_name_plural = verbose_name
def __str__ ( self ) :
return f " { self . user . username } likes { self . article . title } "
# 收藏模型
class Favorite ( BaseModel ) :
user = models . ForeignKey (
settings . AUTH_USER_MODEL ,
verbose_name = _ ( ' user ' ) ,
on_delete = models . CASCADE ) # 关联用户
article = models . ForeignKey (
Article ,
verbose_name = _ ( ' article ' ) ,
on_delete = models . CASCADE ) # 关联文章
class Meta :
unique_together = ( ' user ' , ' article ' ) # 确保一个用户只能对一篇文章收藏一次
verbose_name = _ ( ' favorite ' )
verbose_name_plural = verbose_name
def __str__ ( self ) :
return f " { self . user . username } favorites { self . article . title } "
class BlogSettings ( models . Model ) :
class BlogSettings ( models . Model ) :
site_name = models . CharField (
site_name = models . CharField (
_ ( ' site name ' ) ,
_ ( ' site name ' ) ,
@ -338,19 +407,18 @@ class BlogSettings(models.Model):
show_google_adsense = models . BooleanField ( _ ( ' show adsense ' ) , default = False ) #fkc 是否显示广告
show_google_adsense = models . BooleanField ( _ ( ' show adsense ' ) , default = False ) #fkc 是否显示广告
google_adsense_codes = models . TextField (
google_adsense_codes = models . TextField (
_ ( ' adsense code ' ) , max_length = 2000 , null= True , blank= True , default = ' ' ) #fkc 广告代码
_ ( ' adsense code ' ) , max_length = 2000 , blank= True , default = ' ' ) #fkc 广告代码
open_site_comment = models . BooleanField ( _ ( ' open site comment ' ) , default = True ) #fkc 是否开放评论
open_site_comment = models . BooleanField ( _ ( ' open site comment ' ) , default = True ) #fkc 是否开放评论
comment_need_review = models . BooleanField (
comment_need_review = models . BooleanField (
' 评论是否需要审核 ' , default = False , null = False ) #fkc 评论是否需要审核
' 评论是否需要审核 ' , default = False , null = False ) #fkc 评论是否需要审核
global_header = models . TextField ( " 公共头部 " , null= True , blank= True , default = ' ' ) #fkc 公共头部HTML
global_header = models . TextField ( " 公共头部 " , blank= True , default = ' ' ) #fkc 公共头部HTML
global_footer = models . TextField ( " 公共尾部 " , null= True , blank= True , default = ' ' ) #fkc 公共尾部HTML
global_footer = models . TextField ( " 公共尾部 " , blank= True , default = ' ' ) #fkc 公共尾部HTML
beian_code = models . CharField (
beian_code = models . CharField (
' 备案号 ' ,
' 备案号 ' ,
max_length = 2000 ,
max_length = 2000 ,
null = True ,
blank = True ,
blank = True ,
default = ' ' ) #fkc ICP备案号
default = ' ' ) #fkc ICP备案号
show_gongan_code = models . BooleanField (
show_gongan_code = models . BooleanField (
@ -358,7 +426,6 @@ class BlogSettings(models.Model):
gongan_beiancode = models . TextField (
gongan_beiancode = models . TextField (
' 公安备案号 ' ,
' 公安备案号 ' ,
max_length = 2000 ,
max_length = 2000 ,
null = True ,
blank = True ,
blank = True ,
default = ' ' ) #fkc 公安备案号
default = ' ' ) #fkc 公安备案号