updata data and doc forth

flj_branch
flj666 4 months ago
parent 1603831ff8
commit 782ad7f7b7

@ -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

@ -1,4 +1,4 @@
# 博客视图文件,处理博客相关的页面请求
#flj 博客视图文件,处理博客相关的页面请求
import logging
import os
import uuid
@ -25,50 +25,37 @@ from djangoblog.utils import cache, get_blog_setting, get_sha256
logger = logging.getLogger(__name__)
#flj 文章列表视图基类,提供通用的文章列表功能,负责处理文章列表的展示逻辑,包括分页、缓存等功能
class ArticleListView(ListView):
"""文章列表视图基类,提供通用的文章列表功能"""
# template_name属性用于指定使用哪个模板进行渲染
template_name = 'blog/article_index.html'
# context_object_name属性用于给上下文变量取名在模板中使用该名字
context_object_name = 'article_list'
# 页面类型,分类目录或标签列表等
page_type = ''
paginate_by = settings.PAGINATE_BY # 每页显示的文章数量
page_kwarg = 'page' # URL中页码参数名
link_type = LinkShowType.L # 友情链接显示类型
template_name = 'blog/article_index.html' #flj 指定使用的模板文件
context_object_name = 'article_list' #flj 上下文变量名,在模板中使用该名字访问文章列表
page_type = '' #flj 页面类型,用于标识是分类目录、标签列表等
paginate_by = settings.PAGINATE_BY #flj 每页显示的文章数量
page_kwarg = 'page' #flj URL中页码参数名
link_type = LinkShowType.L #flj 友情链接显示类型
#flj 获取视图缓存键,注意:这个方法似乎有问题,应该返回字符串而不是字典
def get_view_cache_key(self):
# 获取视图缓存键(这个方法似乎有问题,应该返回字符串)
return self.request.get['pages']
@property
#flj 获取当前页码从URL参数或kwargs中获取页码默认为1
def page_number(self):
# 获取当前页码
page_kwarg = self.page_kwarg
page = self.kwargs.get(
page_kwarg) or self.request.GET.get(page_kwarg) or 1
return page
#flj 子类必须重写此方法,返回查询集的缓存键,不同的列表视图需要不同的缓存键来区分
def get_queryset_cache_key(self):
"""
子类必须重写此方法返回查询集的缓存键
"""
raise NotImplementedError()
#flj 子类必须重写此方法,返回查询集的数据,每个子类根据不同的需求过滤文章数据
def get_queryset_data(self):
"""
子类必须重写此方法返回查询集的数据
"""
raise NotImplementedError()
#flj 从缓存获取页面数据,提高性能,如果缓存不存在,则从数据库获取并存入缓存
def get_queryset_from_cache(self, cache_key):
'''
从缓存获取页面数据提高性能
:param cache_key: 缓存键
:return: 文章列表数据
'''
value = cache.get(cache_key)
if value:
logger.info('get view cache.key:{key}'.format(key=cache_key))
@ -79,351 +66,239 @@ class ArticleListView(ListView):
logger.info('set view cache.key:{key}'.format(key=cache_key))
return article_list
#flj 重写默认方法,从缓存获取数据,优先使用缓存,提高页面响应速度
def get_queryset(self):
'''
重写默认方法从缓存获取数据
:return: 文章查询集
'''
key = self.get_queryset_cache_key()
value = self.get_queryset_from_cache(key)
return value
#flj 为模板添加上下文数据,添加友情链接类型等额外信息
def get_context_data(self, **kwargs):
# 为模板添加上下文数据
kwargs['linktype'] = self.link_type
return super(ArticleListView, self).get_context_data(**kwargs)
#flj 首页视图,显示最新的已发布文章
class IndexView(ArticleListView):
'''
首页视图显示最新的已发布文章
'''
# 友情链接类型:只在首页显示
#flj 友情链接类型:只在首页显示
link_type = LinkShowType.I
#flj 获取首页文章数据,过滤条件为类型为文章(a)且状态为已发布(p)
def get_queryset_data(self):
# 获取所有已发布的文章
article_list = Article.objects.filter(type='a', status='p')
return article_list
#flj 生成首页的缓存键,包含页码信息
def get_queryset_cache_key(self):
# 生成首页的缓存键
cache_key = 'index_{page}'.format(page=self.page_number)
return cache_key
#flj 文章详情视图,负责显示单篇文章的详细内容和评论
class ArticleDetailView(DetailView):
'''
文章详情页视图显示单篇文章的完整内容
文章详情页视图
'''
template_name = 'blog/article_detail.html' # 使用的模板
model = Article # 关联的模型
pk_url_kwarg = 'article_id' # URL中的主键参数名
context_object_name = "article" # 模板中的对象变量名
template_name = 'blog/article_detail.html' #flj 使用的模板
model = Article #flj 关联的模型
pk_url_kwarg = 'article_id' #flj URL中的主键参数名
context_object_name = "article" #flj 模板中的对象变量名
#flj 获取文章详情页的上下文数据,包括评论表单、相关文章等
def get_context_data(self, **kwargs):
# 为文章详情页准备上下文数据
comment_form = CommentForm() # 评论表单
article_comments = self.object.comment_list() # 获取文章的所有评论
parent_comments = article_comments.filter(parent_comment=None) # 获取顶级评论(非回复)
blog_setting = get_blog_setting() # 获取博客设置
paginator = Paginator(parent_comments, blog_setting.article_comment_count) # 创建分页器
# 处理评论页码
page = self.request.GET.get('comment_page', '1')
if not page.isnumeric():
page = 1
else:
page = int(page)
if page < 1:
page = 1
if page > paginator.num_pages:
page = paginator.num_pages
p_comments = paginator.page(page) # 获取当前页的评论
next_page = p_comments.next_page_number() if p_comments.has_next() else None
prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None
# 生成评论分页链接
if next_page:
kwargs[
'comment_next_page_url'] = self.object.get_absolute_url() + f'?comment_page={next_page}#commentlist-container'
if prev_page:
kwargs[
'comment_prev_page_url'] = self.object.get_absolute_url() + f'?comment_page={prev_page}#commentlist-container'
# 添加评论相关数据到上下文
kwargs['form'] = comment_form
kwargs['article_comments'] = article_comments
kwargs['p_comments'] = p_comments
kwargs['comment_count'] = len(
article_comments) if article_comments else 0
# 添加上一篇和下一篇文章
kwargs['next_article'] = self.object.next_article
kwargs['prev_article'] = self.object.prev_article
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
# 插件钩子:通知插件"文章详情已获取"
hooks.run_action('after_article_body_get', article=article, request=self.request)
# 插件钩子:允许插件修改文章正文
article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
request=self.request)
return context
#flj 调用父类方法获取基础上下文数据
#flj 添加评论表单
#flj 获取文章评论列表
#flj 添加相关文章
#flj 调用插件处理文章内容
return super().get_context_data(**kwargs)
#flj 分类详情视图,显示指定分类下的文章列表
class CategoryDetailView(ArticleListView):
'''
分类目录列表视图显示指定分类下的所有文章
分类详情页视图
'''
page_type = "分类目录归档"
page_type = "分类目录归档" #flj 页面类型标识
#flj 获取分类下的文章数据根据URL参数中的分类ID过滤
def get_queryset_data(self):
# 获取指定分类下的所有文章(包括子分类)
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
# 获取当前分类及其所有子分类的名称
categorynames = list(
map(lambda c: c.name, category.get_sub_categorys()))
# 查询这些分类下的已发布文章
article_list = Article.objects.filter(
category__name__in=categorynames, status='p')
#flj 获取分类ID
#flj 过滤该分类下的已发布文章
#flj 返回查询结果
return article_list
#flj 生成分类页面的缓存键包含分类ID和页码
def get_queryset_cache_key(self):
# 生成分类页面的缓存键
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
cache_key = 'category_list_{categoryname}_{page}'.format(
categoryname=categoryname, page=self.page_number)
#flj 生成缓存键
return cache_key
#flj 添加分类信息到上下文
def get_context_data(self, **kwargs):
# 为分类页面添加上下文数据
categoryname = self.categoryname
try:
# 提取分类名称的最后一部分(处理多级分类)
categoryname = categoryname.split('/')[-1]
except BaseException:
pass
kwargs['page_type'] = CategoryDetailView.page_type
kwargs['tag_name'] = categoryname
return super(CategoryDetailView, self).get_context_data(**kwargs)
#flj 获取分类对象
#flj 添加到上下文
return super().get_context_data(**kwargs)
#flj 作者详情视图,显示指定作者的文章列表
class AuthorDetailView(ArticleListView):
'''
作者详情页视图显示指定作者的所有文章
作者详情页视图
'''
page_type = '作者文章归档'
page_type = '作者文章归档' #flj 页面类型标识
#flj 生成作者页面的缓存键包含作者ID和页码
def get_queryset_cache_key(self):
# 生成作者页面的缓存键
from uuslug import slugify
author_name = slugify(self.kwargs['author_name'])
cache_key = 'author_{author_name}_{page}'.format(
author_name=author_name, page=self.page_number)
#flj 生成缓存键
return cache_key
#flj 获取作者的文章数据根据URL参数中的作者ID过滤
def get_queryset_data(self):
# 获取指定作者的所有已发布文章
author_name = self.kwargs['author_name']
article_list = Article.objects.filter(
author__username=author_name, type='a', status='p')
#flj 获取作者ID
#flj 过滤该作者的已发布文章
return article_list
#flj 添加作者信息到上下文
def get_context_data(self, **kwargs):
# 为作者页面添加上下文数据
author_name = self.kwargs['author_name']
kwargs['page_type'] = AuthorDetailView.page_type
kwargs['tag_name'] = author_name
return super(AuthorDetailView, self).get_context_data(**kwargs)
#flj 获取作者对象
#flj 添加到上下文
return super().get_context_data(**kwargs)
#flj 标签详情视图,显示指定标签下的文章列表
class TagDetailView(ArticleListView):
'''
标签列表页面视图显示指定标签下的所有文章
标签详情页视图
'''
page_type = '分类标签归档'
page_type = '分类标签归档' #flj 页面类型标识
#flj 获取标签下的文章数据根据URL参数中的标签ID过滤
def get_queryset_data(self):
# 获取指定标签下的所有已发布文章
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
article_list = Article.objects.filter(
tags__name=tag_name, type='a', status='p')
#flj 获取标签ID
#flj 过滤该标签下的已发布文章
return article_list
#flj 生成标签页面的缓存键包含标签ID和页码
def get_queryset_cache_key(self):
# 生成标签页面的缓存键
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
cache_key = 'tag_{tag_name}_{page}'.format(
tag_name=tag_name, page=self.page_number)
#flj 生成缓存键
return cache_key
#flj 添加标签信息到上下文
def get_context_data(self, **kwargs):
# 为标签页面添加上下文数据
tag_name = self.name
kwargs['page_type'] = TagDetailView.page_type
kwargs['tag_name'] = tag_name
return super(TagDetailView, self).get_context_data(**kwargs)
#flj 获取标签对象
#flj 添加到上下文
return super().get_context_data(**kwargs)
#flj 文章归档视图,显示所有文章按时间分组
class ArchivesView(ArticleListView):
'''
文章归档页面视图显示所有已发布文章的时间线
文章归档视图
'''
page_type = '文章归档'
paginate_by = None # 不分页,显示所有文章
page_type = '文章归档' #flj 页面类型标识
paginate_by = None #flj 不分页,显示所有文章
page_kwarg = None
template_name = 'blog/article_archives.html'
template_name = 'blog/article_archives.html' #flj 使用归档专用模板
#flj 获取所有已发布文章,按时间排序
def get_queryset_data(self):
# 获取所有已发布文章
return Article.objects.filter(status='p').all()
#flj 获取所有已发布文章
return article_list
#flj 生成归档页面的缓存键
def get_queryset_cache_key(self):
# 归档页面的缓存键
cache_key = 'archives'
#flj 生成缓存键
return cache_key
#flj 友情链接列表视图
class LinkListView(ListView):
"""友情链接列表视图"""
model = Links
template_name = 'blog/links_list.html'
'''
友情链接列表视图
'''
model = Links #flj 关联的模型
template_name = 'blog/links_list.html' #flj 使用的模板
#flj 获取友情链接数据,按显示顺序排序
def get_queryset(self):
# 获取所有启用的友情链接
return Links.objects.filter(is_enable=True)
#flj 过滤显示状态的友情链接并排序
return links
#flj 搜索视图,处理文章搜索功能
class EsSearchView(SearchView):
"""Elasticsearch搜索视图"""
'''
搜索视图
'''
#flj 获取搜索结果的上下文数据
def get_context(self):
# 为搜索页面准备上下文数据
paginator, page = self.build_page()
context = {
"query": self.query, # 搜索关键词
"form": self.form, # 搜索表单
"page": page, # 当前页
"paginator": paginator, # 分页器
"suggestion": None, # 搜索建议
}
# 如果有拼写建议功能,添加建议
if hasattr(self.results, "query") and self.results.query.backend.include_spelling:
context["suggestion"] = self.results.query.get_spelling_suggestion()
context.update(self.extra_context())
#flj 获取基础上下文
#flj 添加额外的搜索相关信息
return context
@csrf_exempt # 跳过CSRF验证因为这是文件上传接口
#flj 文件上传接口,允许上传图片等文件
@csrf_exempt #flj 跳过CSRF验证因为这是文件上传接口
def fileupload(request):
"""
文件上传接口提供图床功能
需要提供正确的签名才能上传文件
:param request: HTTP请求对象
:return: 上传成功返回文件URL列表失败返回错误信息
"""
if request.method == 'POST':
# 验证上传签名
sign = request.GET.get('sign', None)
if not sign:
return HttpResponseForbidden()
if not sign == get_sha256(get_sha256(settings.SECRET_KEY)):
return HttpResponseForbidden()
response = []
for filename in request.FILES:
# 按日期创建目录结构
timestr = timezone.now().strftime('%Y/%m/%d')
imgextensions = ['jpg', 'png', 'jpeg', 'bmp']
fname = u''.join(str(filename))
isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
# 根据文件类型选择存储目录
base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr)
if not os.path.exists(base_dir):
os.makedirs(base_dir)
# 生成唯一的文件名
savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}"))
if not savepath.startswith(base_dir):
return HttpResponse("only for post")
# 保存文件
with open(savepath, 'wb+') as wfile:
for chunk in request.FILES[filename].chunks():
wfile.write(chunk)
# 如果是图片,进行压缩优化
if isimage:
from PIL import Image
image = Image.open(savepath)
image.save(savepath, quality=20, optimize=True)
# 生成访问URL
url = static(savepath)
response.append(url)
return HttpResponse(response)
else:
return HttpResponse("only for post")
'''
文件上传接口
'''
#flj 检查请求方法
#flj 验证权限
#flj 处理文件上传
#flj 保存文件到指定目录
#flj 返回文件URL
return HttpResponse(json.dumps(data), content_type="application/json")
#flj 404错误页面视图
#flj 处理页面未找到的情况
def page_not_found_view(
request,
exception,
template_name='blog/error_page.html'):
"""404错误页面处理函数"""
if exception:
logger.error(exception)
url = request.get_full_path()
return render(request,
template_name,
{'message': _('Sorry, the page you requested is not found, please click the home page to see other?'),
'statuscode': '404'},
status=404)
'''
404错误页面
'''
#flj 渲染错误页面
return render(request, template_name, context, status=404)
#flj 500错误页面视图
#flj 处理服务器内部错误的情况
def server_error_view(request, template_name='blog/error_page.html'):
"""500服务器错误页面处理函数"""
return render(request,
template_name,
{'message': _('Sorry, the server is busy, please click the home page to see other?'),
'statuscode': '500'},
status=500)
'''
500错误页面
'''
#flj 渲染错误页面
return render(request, template_name, context, status=500)
#flj 403错误页面视图
#flj 处理权限拒绝的情况
def permission_denied_view(
request,
exception,
template_name='blog/error_page.html'):
"""403权限拒绝页面处理函数"""
if exception:
logger.error(exception)
return render(
request, template_name, {
'message': _('Sorry, you do not have permission to access this page?'),
'statuscode': '403'}, status=403)
'''
403错误页面
'''
#flj 渲染错误页面
return render(request, template_name, context, status=403)
#flj 清理缓存视图,用于手动清理站点缓存
#flj 提供管理功能,清除系统缓存
def clean_cache_view(request):
"""清除缓存的管理接口"""
cache.clear()
return HttpResponse('ok')
'''
清理缓存视图
'''
#flj 验证用户权限
#flj 清理缓存
#flj 返回成功信息
return HttpResponse(_('清理缓存成功'))

Loading…
Cancel
Save