@ -5,7 +5,6 @@ import uuid
from django . conf import settings
from django . core . paginator import Paginator
from django . http import HttpResponse , HttpResponseForbidden
from django . http import JsonResponse , HttpResponseBadRequest
from django . shortcuts import get_object_or_404
from django . shortcuts import render
from django . templatetags . static import static
@ -22,10 +21,13 @@ from djangoblog.plugin_manage import hooks
from djangoblog . plugin_manage . hook_constants import ARTICLE_CONTENT_HOOK_NAME
from djangoblog . utils import cache , get_blog_setting , get_sha256
logger = logging . getLogger ( __name__ )
logger = logging . getLogger ( __name__ ) # 创建日志记录器
class ArticleListView ( ListView ) :
"""
文章列表视图基类 , 提供分页和缓存功能
"""
# template_name属性用于指定使用哪个模板进行渲染
template_name = ' blog/article_index.html '
@ -34,15 +36,21 @@ class ArticleListView(ListView):
# 页面类型,分类目录或标签列表等
page_type = ' '
paginate_by = settings . PAGINATE_BY
page_kwarg = ' page '
link_type = LinkShowType . L
paginate_by = settings . PAGINATE_BY # 每页文章数量
page_kwarg = ' page ' # 分页参数名
link_type = LinkShowType . L # 链接显示类型
def get_view_cache_key ( self ) :
"""
获取视图缓存key
"""
return self . request . get [ ' pages ' ]
@property
def page_number ( self ) :
"""
获取当前页码
"""
page_kwarg = self . page_kwarg
page = self . kwargs . get (
page_kwarg ) or self . request . GET . get ( page_kwarg ) or 1
@ -60,25 +68,11 @@ class ArticleListView(ListView):
"""
raise NotImplementedError ( )
def get_sort ( self ) :
sort = self . request . GET . get ( ' sort ' , ' latest ' )
return sort if sort in ( ' latest ' , ' hot ' ) else ' latest '
def order_queryset ( self , qs ) :
sort = self . get_sort ( )
if sort == ' hot ' :
from django . utils import timezone
from datetime import timedelta
week_ago = timezone . now ( ) - timedelta ( days = 7 )
qs = qs . filter ( pub_time__gte = week_ago )
return qs . order_by ( ' -article_order ' , ' -views ' , ' -pub_time ' )
return qs . order_by ( ' -article_order ' , ' -pub_time ' )
def get_queryset_from_cache ( self , cache_key ) :
'''
缓存页面数据
: param cache_key : 缓存key
: return :
: return : 查询结果集
'''
value = cache . get ( cache_key )
if value :
@ -93,37 +87,45 @@ class ArticleListView(ListView):
def get_queryset ( self ) :
'''
重写默认 , 从缓存获取数据
: return :
: return : 查询结果集
'''
key = self . get_queryset_cache_key ( )
value = self . get_queryset_from_cache ( key )
return value
def get_context_data ( self , * * kwargs ) :
"""
添加额外的上下文数据
"""
kwargs [ ' linktype ' ] = self . link_type
kwargs [ ' sort ' ] = self . get_sort ( )
return super ( ArticleListView , self ) . get_context_data ( * * kwargs )
class IndexView ( ArticleListView ) :
'''
首页
首页 视图
'''
# 友情链接类型
link_type = LinkShowType . I
def get_queryset_data ( self ) :
qs = Article . objects . filter ( type = ' a ' , status = ' p ' )
return self . order_queryset ( qs )
"""
获取首页文章数据
"""
article_list = Article . objects . filter ( type = ' a ' , status = ' p ' )
return article_list
def get_queryset_cache_key ( self ) :
cache_key = ' index_ {page} _sort_ {sort} ' . format ( page = self . page_number , sort = self . get_sort ( ) )
"""
获取首页缓存key
"""
cache_key = ' index_ {page} ' . format ( page = self . page_number )
return cache_key
class ArticleDetailView ( DetailView ) :
'''
文章详情页面
文章详情页面 视图
'''
template_name = ' blog/article_detail.html '
model = Article
@ -131,11 +133,15 @@ class ArticleDetailView(DetailView):
context_object_name = " article "
def get_context_data ( self , * * kwargs ) :
comment_form = CommentForm ( )
"""
获取文章详情页的上下文数据
"""
comment_form = CommentForm ( ) # 评论表单
article_comments = self . object . comment_list ( )
parent_comments = article_comments . filter ( parent_comment = None )
blog_setting = get_blog_setting ( )
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 ( ) :
@ -148,6 +154,7 @@ class ArticleDetailView(DetailView):
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
@ -157,12 +164,15 @@ class ArticleDetailView(DetailView):
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
@ -175,24 +185,26 @@ class ArticleDetailView(DetailView):
# Action Hook, 通知插件"文章详情已获取"
hooks . run_action ( ' after_article_body_get ' , article = article , request = self . request )
liked_ids = self . request . session . get ( ' liked_articles ' , [ ] )
favorited_ids = self . request . session . get ( ' favorited_articles ' , [ ] )
context [ ' has_liked ' ] = (
self . request . user . is_authenticated and article . like_users . filter ( pk = self . request . user . pk ) . exists ( )
) or ( article . id in liked_ids )
context [ ' has_favorited ' ] = (
self . request . user . is_authenticated and article . favorite_users . filter ( pk = self . request . user . pk ) . exists ( )
) or ( article . id in favorited_ids )
<< << << < HEAD
== == == =
# Filter Hook, 允许插件修改文章正文
article . body = hooks . apply_filters ( ARTICLE_CONTENT_HOOK_NAME , article . body , article = article ,
request = self . request )
>> >> >> > 8 d6f4db66b485c068d8b4da7960c4e2ac81e00c2
return context
class CategoryDetailView ( ArticleListView ) :
'''
分类目录列表
分类目录列表 视图
'''
page_type = " 分类目录归档 "
def get_queryset_data ( self ) :
"""
获取分类文章数据
"""
slug = self . kwargs [ ' category_name ' ]
category = get_object_or_404 ( Category , slug = slug )
@ -200,21 +212,26 @@ class CategoryDetailView(ArticleListView):
self . categoryname = categoryname
categorynames = list (
map ( lambda c : c . name , category . get_sub_categorys ( ) ) )
qs = Article . objects . filter (
article_list = Article . objects . filter (
category__name__in = categorynames , status = ' p ' )
return self . order_queryset ( qs )
return article_list
def get_queryset_cache_key ( self ) :
"""
获取分类列表缓存key
"""
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} _sort_{sort} '. format (
categoryname = categoryname , page = self . page_number , sort = self . get_sort ( ) )
cache_key = ' category_list_ {categoryname} _ {page} '. format (
categoryname = categoryname , page = self . page_number )
return cache_key
def get_context_data ( self , * * kwargs ) :
"""
添加分类相关上下文数据
"""
categoryname = self . categoryname
try :
categoryname = categoryname . split ( ' / ' ) [ - 1 ]
@ -227,24 +244,33 @@ class CategoryDetailView(ArticleListView):
class AuthorDetailView ( ArticleListView ) :
'''
作者详情页
作者详情页 视图
'''
page_type = ' 作者文章归档 '
def get_queryset_cache_key ( self ) :
"""
获取作者文章列表缓存key
"""
from uuslug import slugify
author_name = slugify ( self . kwargs [ ' author_name ' ] )
cache_key = ' author_ {author_name} _ {page} _sort_ {sort} ' . format (
author_name = author_name , page = self . page_number , sort = self . get_sort ( ) )
cache_key = ' author_ {author_name} _ {page} '. format (
author_name = author_name , page = self . page_number )
return cache_key
def get_queryset_data ( self ) :
"""
获取作者文章数据
"""
author_name = self . kwargs [ ' author_name ' ]
qs = Article . objects . filter (
article_list = Article . objects . filter (
author__username = author_name , type = ' a ' , status = ' p ' )
return self . order_queryset ( qs )
return article_list
def get_context_data ( self , * * kwargs ) :
"""
添加作者相关上下文数据
"""
author_name = self . kwargs [ ' author_name ' ]
kwargs [ ' page_type ' ] = AuthorDetailView . page_type
kwargs [ ' tag_name ' ] = author_name
@ -253,29 +279,38 @@ class AuthorDetailView(ArticleListView):
class TagDetailView ( ArticleListView ) :
'''
标签列表页面
标签列表页面 视图
'''
page_type = ' 分类标签归档 '
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
qs = Article . objects . filter (
article_list = Article . objects . filter (
tags__name = tag_name , type = ' a ' , status = ' p ' )
return self . order_queryset ( qs )
return article_list
def get_queryset_cache_key ( self ) :
"""
获取标签列表缓存key
"""
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} _sort_{sort} '. format (
tag_name = tag_name , page = self . page_number , sort = self . get_sort ( ) )
cache_key = ' tag_ {tag_name} _ {page} '. format (
tag_name = tag_name , page = self . page_number )
return cache_key
def get_context_data ( self , * * kwargs ) :
"""
添加标签相关上下文数据
"""
# tag_name = self.kwargs['tag_name']
tag_name = self . name
kwargs [ ' page_type ' ] = TagDetailView . page_type
@ -285,31 +320,49 @@ class TagDetailView(ArticleListView):
class ArchivesView ( ArticleListView ) :
'''
文章归档页面
文章归档页面 视图
'''
page_type = ' 文章归档 '
paginate_by = None
page_kwarg = None
paginate_by = None # 不分页
page_kwarg = None # 无分页参数
template_name = ' blog/article_archives.html '
def get_queryset_data ( self ) :
"""
获取所有已发布文章
"""
return Article . objects . filter ( status = ' p ' ) . all ( )
def get_queryset_cache_key ( self ) :
"""
获取归档页面缓存key
"""
cache_key = ' archives '
return cache_key
class LinkListView ( ListView ) :
"""
友情链接列表视图
"""
model = Links
template_name = ' blog/links_list.html '
def get_queryset ( self ) :
"""
获取启用的友情链接
"""
return Links . objects . filter ( is_enable = True )
class EsSearchView ( SearchView ) :
"""
Elasticsearch搜索视图
"""
def get_context ( self ) :
"""
获取搜索上下文数据
"""
paginator , page = self . build_page ( )
context = {
" query " : self . query ,
@ -328,31 +381,37 @@ class EsSearchView(SearchView):
@csrf_exempt
def fileupload ( request ) :
"""
该方法需自己写调用端来上传图片, 该方法仅提供图床功能
: param request :
: return :
文件上传视图, 该方法需自己写调用端来上传图片, 该方法仅提供图床功能
: 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 )
@ -369,6 +428,9 @@ def page_not_found_view(
request ,
exception ,
template_name = ' blog/error_page.html ' ) :
"""
404 页面不存在视图
"""
if exception :
logger . error ( exception )
url = request . get_full_path ( )
@ -379,51 +441,10 @@ def page_not_found_view(
status = 404 )
@csrf_exempt
def like_article ( request , article_id ) :
if request . method != ' POST ' :
return HttpResponseBadRequest ( ' only post ' )
article = get_object_or_404 ( Article , pk = article_id )
if request . user . is_authenticated :
if article . like_users . filter ( pk = request . user . pk ) . exists ( ) :
return JsonResponse ( { ' ok ' : False , ' message ' : ' 已点赞 ' , ' like_count ' : article . like_count } )
article . like_users . add ( request . user )
article . like_count + = 1
article . save ( update_fields = [ ' like_count ' ] )
return JsonResponse ( { ' ok ' : True , ' message ' : ' 点赞成功 ' , ' like_count ' : article . like_count } )
liked = request . session . get ( ' liked_articles ' , [ ] )
if article . id in liked :
return JsonResponse ( { ' ok ' : False , ' message ' : ' 已点赞 ' , ' like_count ' : article . like_count } )
liked . append ( article . id )
request . session [ ' liked_articles ' ] = liked
article . like_count + = 1
article . save ( update_fields = [ ' like_count ' ] )
return JsonResponse ( { ' ok ' : True , ' message ' : ' 点赞成功 ' , ' like_count ' : article . like_count } )
@csrf_exempt
def favorite_article ( request , article_id ) :
if request . method != ' POST ' :
return HttpResponseBadRequest ( ' only post ' )
article = get_object_or_404 ( Article , pk = article_id )
if request . user . is_authenticated :
if article . favorite_users . filter ( pk = request . user . pk ) . exists ( ) :
return JsonResponse ( { ' ok ' : False , ' message ' : ' 已收藏 ' , ' favorite_count ' : article . favorite_count } )
article . favorite_users . add ( request . user )
article . favorite_count + = 1
article . save ( update_fields = [ ' favorite_count ' ] )
return JsonResponse ( { ' ok ' : True , ' message ' : ' 收藏成功 ' , ' favorite_count ' : article . favorite_count } )
favorited = request . session . get ( ' favorited_articles ' , [ ] )
if article . id in favorited :
return JsonResponse ( { ' ok ' : False , ' message ' : ' 已收藏 ' , ' favorite_count ' : article . favorite_count } )
favorited . append ( article . id )
request . session [ ' favorited_articles ' ] = favorited
article . favorite_count + = 1
article . save ( update_fields = [ ' favorite_count ' ] )
return JsonResponse ( { ' ok ' : True , ' message ' : ' 收藏成功 ' , ' favorite_count ' : article . favorite_count } )
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? ' ) ,
@ -435,6 +456,9 @@ def permission_denied_view(
request ,
exception ,
template_name = ' blog/error_page.html ' ) :
"""
403 权限拒绝视图
"""
if exception :
logger . error ( exception )
return render (
@ -444,5 +468,9 @@ def permission_denied_view(
def clean_cache_view ( request ) :
"""
清理缓存视图
"""
cache . clear ( )
return HttpResponse ( ' ok ' )