|
|
|
|
@ -1,3 +1,4 @@
|
|
|
|
|
#sxc 该模块基于 Django 框架,实现博客系统的各类视图功能,包括文章列表、详情、分类、标签、搜索、文件上传、错误页处理等,同时集成缓存、插件钩子等机制,保障页面渲染和业务逻辑的稳定性与可扩展性。
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import uuid
|
|
|
|
|
@ -26,6 +27,7 @@ logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
class ArticleListView(ListView):
|
|
|
|
|
# template_name属性用于指定使用哪个模板进行渲染
|
|
|
|
|
#sxc 文章列表视图基类,封装分页、缓存、上下文处理等通用逻辑,为子类(首页、分类、标签等列表)提供基础支持
|
|
|
|
|
template_name = 'blog/article_index.html'
|
|
|
|
|
|
|
|
|
|
# context_object_name属性用于给上下文变量取名(在模板中使用该名字)
|
|
|
|
|
@ -42,6 +44,7 @@ class ArticleListView(ListView):
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def page_number(self):
|
|
|
|
|
#sxc 获取当前分页页码,支持从 URL 参数或 kwargs 中提取
|
|
|
|
|
page_kwarg = self.page_kwarg
|
|
|
|
|
page = self.kwargs.get(
|
|
|
|
|
page_kwarg) or self.request.GET.get(page_kwarg) or 1
|
|
|
|
|
@ -51,12 +54,14 @@ class ArticleListView(ListView):
|
|
|
|
|
"""
|
|
|
|
|
子类重写.获得queryset的缓存key
|
|
|
|
|
"""
|
|
|
|
|
#sxc 抽象方法,子类需重写以定义查询集的缓存键生成规则,用于缓存数据的标识
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
"""
|
|
|
|
|
子类重写.获取queryset的数据
|
|
|
|
|
"""
|
|
|
|
|
#sxc 抽象方法,子类需重写以实现具体的查询集数据获取逻辑(如筛选文章条件)
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
def get_queryset_from_cache(self, cache_key):
|
|
|
|
|
@ -65,6 +70,7 @@ class ArticleListView(ListView):
|
|
|
|
|
:param cache_key: 缓存key
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
#sxc 从缓存中获取查询集数据,若缓存未命中则调用 get_queryset_data 获取并缓存
|
|
|
|
|
value = cache.get(cache_key)
|
|
|
|
|
if value:
|
|
|
|
|
logger.info('get view cache.key:{key}'.format(key=cache_key))
|
|
|
|
|
@ -80,6 +86,7 @@ class ArticleListView(ListView):
|
|
|
|
|
重写默认,从缓存获取数据
|
|
|
|
|
:return:
|
|
|
|
|
'''
|
|
|
|
|
#sxc 重写父类方法,优先从缓存获取查询集数据,减少数据库查询
|
|
|
|
|
key = self.get_queryset_cache_key()
|
|
|
|
|
value = self.get_queryset_from_cache(key)
|
|
|
|
|
return value
|
|
|
|
|
@ -94,13 +101,16 @@ class IndexView(ArticleListView):
|
|
|
|
|
首页
|
|
|
|
|
'''
|
|
|
|
|
# 友情链接类型
|
|
|
|
|
#sxc 博客首页视图,继承自 ArticleListView 基类,专注于展示所有已发布文章,配置首页专属友情链接类型
|
|
|
|
|
link_type = LinkShowType.I
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
#sxc 实现父类抽象方法,获取首页需展示的文章数据:筛选类型为 “普通文章(a)” 且状态为 “已发布(p)” 的文章
|
|
|
|
|
article_list = Article.objects.filter(type='a', status='p')
|
|
|
|
|
return article_list
|
|
|
|
|
|
|
|
|
|
def get_queryset_cache_key(self):
|
|
|
|
|
#sxc 实现父类抽象方法,生成首页文章列表的缓存键,格式为 “index_页码”,确保不同分页数据的缓存隔离
|
|
|
|
|
cache_key = 'index_{page}'.format(page=self.page_number)
|
|
|
|
|
return cache_key
|
|
|
|
|
|
|
|
|
|
@ -109,18 +119,21 @@ class ArticleDetailView(DetailView):
|
|
|
|
|
'''
|
|
|
|
|
文章详情页面
|
|
|
|
|
'''
|
|
|
|
|
template_name = 'blog/article_detail.html'
|
|
|
|
|
model = Article
|
|
|
|
|
pk_url_kwarg = 'article_id'
|
|
|
|
|
context_object_name = "article"
|
|
|
|
|
#sxc 文章详情页视图,继承自 Django 的 DetailView,负责单篇文章的完整信息展示,包括内容、评论、上下篇导航等
|
|
|
|
|
template_name = 'blog/article_detail.html' #sxc 指定详情页渲染模板路径
|
|
|
|
|
model = Article #sxc 关联的数据模型为 Article,自动从数据库获取文章数据
|
|
|
|
|
pk_url_kwarg = 'article_id' #sxc 指定 URL 中用于匹配文章主键的参数名为 “article_id”
|
|
|
|
|
context_object_name = "article" #sxc 设定模板中访问文章对象的变量名为 “article”
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
#sxc 重写父类方法,构建详情页所需的完整上下文数据,包括评论表单、评论分页、上下篇文章等
|
|
|
|
|
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)
|
|
|
|
|
#sxc 处理评论分页页码:从 URL 参数获取,非数字 / 越界时自动修正为合法值
|
|
|
|
|
page = self.request.GET.get('comment_page', '1')
|
|
|
|
|
if not page.isnumeric():
|
|
|
|
|
page = 1
|
|
|
|
|
@ -130,31 +143,34 @@ class ArticleDetailView(DetailView):
|
|
|
|
|
page = 1
|
|
|
|
|
if page > paginator.num_pages:
|
|
|
|
|
page = paginator.num_pages
|
|
|
|
|
|
|
|
|
|
#sxc 获取当前页的评论数据,并计算上下页页码
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
#sxc 构建评论上下页的跳转 URL,包含锚点 “#commentlist-container” 以定位到评论区
|
|
|
|
|
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'
|
|
|
|
|
#sxc 向上下文注入评论相关数据:表单、所有评论、当前页评论、评论总数
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
#sxc 向上下文注入当前文章的上下篇文章对象,用于导航
|
|
|
|
|
kwargs['next_article'] = self.object.next_article
|
|
|
|
|
kwargs['prev_article'] = self.object.prev_article
|
|
|
|
|
|
|
|
|
|
#sxc 调用父类方法获取基础上下文,再扩展插件相关逻辑
|
|
|
|
|
context = super(ArticleDetailView, self).get_context_data(**kwargs)
|
|
|
|
|
article = self.object
|
|
|
|
|
# Action Hook, 通知插件"文章详情已获取"
|
|
|
|
|
#sxc 触发 “文章详情已获取” 的插件动作钩子,通知所有注册插件执行对应逻辑(如统计阅读量
|
|
|
|
|
hooks.run_action('after_article_body_get', article=article, request=self.request)
|
|
|
|
|
# # Filter Hook, 允许插件修改文章正文
|
|
|
|
|
#sxc 触发 “文章内容过滤” 的插件钩子,允许插件修改文章正文(如添加水印、解析特殊语法)
|
|
|
|
|
article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
|
|
|
|
|
request=self.request)
|
|
|
|
|
|
|
|
|
|
@ -165,31 +181,36 @@ class CategoryDetailView(ArticleListView):
|
|
|
|
|
'''
|
|
|
|
|
分类目录列表
|
|
|
|
|
'''
|
|
|
|
|
#sxc 分类目录文章列表视图,按 “分类” 维度筛选文章,展示指定分类及下属子分类的所有已发布文章
|
|
|
|
|
page_type = "分类目录归档"
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
#sxc 实现父类抽象方法,按分类筛选文章:先获取分类对象,再查询该分类及子分类下的已发布文章
|
|
|
|
|
slug = self.kwargs['category_name']
|
|
|
|
|
category = get_object_or_404(Category, slug=slug)
|
|
|
|
|
|
|
|
|
|
#sxc 获取当前分类及所有子分类的名称列表(用于多分类联合筛选)
|
|
|
|
|
categoryname = category.name
|
|
|
|
|
self.categoryname = categoryname
|
|
|
|
|
categorynames = list(
|
|
|
|
|
map(lambda c: c.name, category.get_sub_categorys()))
|
|
|
|
|
#sxc 筛选 “属于目标分类 / 子分类” 且 “状态为已发布(p)” 的文章
|
|
|
|
|
article_list = Article.objects.filter(
|
|
|
|
|
category__name__in=categorynames, status='p')
|
|
|
|
|
return article_list
|
|
|
|
|
|
|
|
|
|
def get_queryset_cache_key(self):
|
|
|
|
|
#sxc 实现父类抽象方法,生成分类列表的专属缓存键,确保不同分类、不同分页的缓存隔离
|
|
|
|
|
slug = self.kwargs['category_name']
|
|
|
|
|
category = get_object_or_404(Category, slug=slug)
|
|
|
|
|
categoryname = category.name
|
|
|
|
|
self.categoryname = categoryname
|
|
|
|
|
#sxc 缓存键格式:category_list_分类名_页码(如 “category_list_技术_2”)
|
|
|
|
|
cache_key = 'category_list_{categoryname}_{page}'.format(
|
|
|
|
|
categoryname=categoryname, page=self.page_number)
|
|
|
|
|
return cache_key
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
|
|
|
|
|
#sxc 重写父类方法,向模板上下文注入分类相关标识,用于页面个性化渲染
|
|
|
|
|
categoryname = self.categoryname
|
|
|
|
|
try:
|
|
|
|
|
categoryname = categoryname.split('/')[-1]
|
|
|
|
|
@ -204,9 +225,11 @@ class AuthorDetailView(ArticleListView):
|
|
|
|
|
'''
|
|
|
|
|
作者详情页
|
|
|
|
|
'''
|
|
|
|
|
#sxc 作者文章列表视图,按 “作者” 维度筛选文章,展示指定作者发布的所有已发布文章
|
|
|
|
|
page_type = '作者文章归档'
|
|
|
|
|
|
|
|
|
|
def get_queryset_cache_key(self):
|
|
|
|
|
#sxc 实现父类抽象方法,生成作者列表的专属缓存键,结合作者别名和页码确保缓存隔离
|
|
|
|
|
from uuslug import slugify
|
|
|
|
|
author_name = slugify(self.kwargs['author_name'])
|
|
|
|
|
cache_key = 'author_{author_name}_{page}'.format(
|
|
|
|
|
@ -214,12 +237,15 @@ class AuthorDetailView(ArticleListView):
|
|
|
|
|
return cache_key
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
#sxc 实现父类抽象方法,按作者筛选文章:查询指定作者发布的已发布文章
|
|
|
|
|
author_name = self.kwargs['author_name']
|
|
|
|
|
#sxc 筛选 “作者用户名为目标值”“类型为普通文章(a)”“状态为已发布(p)” 的文章
|
|
|
|
|
article_list = Article.objects.filter(
|
|
|
|
|
author__username=author_name, type='a', status='p')
|
|
|
|
|
return article_list
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
#sxc 重写父类方法,向上下文注入作者相关标识,用于页面渲染
|
|
|
|
|
author_name = self.kwargs['author_name']
|
|
|
|
|
kwargs['page_type'] = AuthorDetailView.page_type
|
|
|
|
|
kwargs['tag_name'] = author_name
|
|
|
|
|
@ -230,6 +256,7 @@ class TagDetailView(ArticleListView):
|
|
|
|
|
'''
|
|
|
|
|
标签列表页面
|
|
|
|
|
'''
|
|
|
|
|
#sxc 标签文章列表视图,按 “标签” 维度筛选文章,展示关联指定标签的所有已发布文章
|
|
|
|
|
page_type = '分类标签归档'
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
@ -237,11 +264,13 @@ class TagDetailView(ArticleListView):
|
|
|
|
|
tag = get_object_or_404(Tag, slug=slug)
|
|
|
|
|
tag_name = tag.name
|
|
|
|
|
self.name = tag_name
|
|
|
|
|
#sxc 筛选 “关联目标标签”“类型为普通文章(a)”“状态为已发布(p)” 的文章
|
|
|
|
|
article_list = Article.objects.filter(
|
|
|
|
|
tags__name=tag_name, type='a', status='p')
|
|
|
|
|
return article_list
|
|
|
|
|
|
|
|
|
|
def get_queryset_cache_key(self):
|
|
|
|
|
#sxc 实现父类抽象方法,生成标签列表的专属缓存键,确保不同标签、不同分页的缓存隔离
|
|
|
|
|
slug = self.kwargs['tag_name']
|
|
|
|
|
tag = get_object_or_404(Tag, slug=slug)
|
|
|
|
|
tag_name = tag.name
|
|
|
|
|
@ -251,6 +280,7 @@ class TagDetailView(ArticleListView):
|
|
|
|
|
return cache_key
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
#sxc 重写父类方法,向上下文注入标签相关标识,用于页面个性化渲染
|
|
|
|
|
# tag_name = self.kwargs['tag_name']
|
|
|
|
|
tag_name = self.name
|
|
|
|
|
kwargs['page_type'] = TagDetailView.page_type
|
|
|
|
|
@ -262,29 +292,36 @@ class ArchivesView(ArticleListView):
|
|
|
|
|
'''
|
|
|
|
|
文章归档页面
|
|
|
|
|
'''
|
|
|
|
|
#sxc 文章归档页面视图,展示所有已发布文章的归档信息(按时间等维度聚合),不分页
|
|
|
|
|
page_type = '文章归档'
|
|
|
|
|
paginate_by = None
|
|
|
|
|
page_kwarg = None
|
|
|
|
|
template_name = 'blog/article_archives.html'
|
|
|
|
|
|
|
|
|
|
def get_queryset_data(self):
|
|
|
|
|
#sxc 获取所有状态为 “已发布(p)” 的文章,用于归档展示
|
|
|
|
|
return Article.objects.filter(status='p').all()
|
|
|
|
|
|
|
|
|
|
def get_queryset_cache_key(self):
|
|
|
|
|
#sxc 归档页缓存键固定为 'archives'(因不分页,无需页码参数)
|
|
|
|
|
cache_key = 'archives'
|
|
|
|
|
return cache_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LinkListView(ListView):
|
|
|
|
|
#sxc 友情链接列表视图,展示所有启用的友情链接
|
|
|
|
|
model = Links
|
|
|
|
|
template_name = 'blog/links_list.html'
|
|
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
#sxc 筛选出所有 “已启用(is_enable=True)” 的友情链接
|
|
|
|
|
return Links.objects.filter(is_enable=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EsSearchView(SearchView):
|
|
|
|
|
#sxc 基于 Elasticsearch 的搜索视图,处理用户搜索请求并返回带分页和拼写建议的结果
|
|
|
|
|
def get_context(self):
|
|
|
|
|
#sxc 构建搜索结果页面的上下文数据,包含查询词、分页信息和拼写建议
|
|
|
|
|
paginator, page = self.build_page()
|
|
|
|
|
context = {
|
|
|
|
|
"query": self.query,
|
|
|
|
|
@ -293,6 +330,7 @@ class EsSearchView(SearchView):
|
|
|
|
|
"paginator": paginator,
|
|
|
|
|
"suggestion": None,
|
|
|
|
|
}
|
|
|
|
|
#sxc 若搜索引擎支持拼写纠错,获取并添加拼写建议
|
|
|
|
|
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())
|
|
|
|
|
@ -307,46 +345,57 @@ def fileupload(request):
|
|
|
|
|
:param request:
|
|
|
|
|
:return:
|
|
|
|
|
"""
|
|
|
|
|
#sxc 处理文件上传(图床功能),支持图片压缩和签名验证,仅允许 POST 请求
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
sign = request.GET.get('sign', None)
|
|
|
|
|
sign = request.GET.get('sign', None) #sxc 获取 URL 中的签名参数(用于权限验证)
|
|
|
|
|
#sxc 验证签名合法性:签名不存在或与系统计算值不符则拒绝访问
|
|
|
|
|
if not sign:
|
|
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
return HttpResponseForbidden() #sxc 无签名时返回 403
|
|
|
|
|
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))
|
|
|
|
|
return HttpResponseForbidden() #sxc 签名不匹配时返回 403
|
|
|
|
|
response = [] #sxc 存储上传文件的访问 URL 列表
|
|
|
|
|
for filename in request.FILES: #sxc 遍历所有上传的文件
|
|
|
|
|
timestr = timezone.now().strftime('%Y/%m/%d')#sxc 按当前日期生成存储目录(如 2023/10/01)
|
|
|
|
|
imgextensions = ['jpg', 'png', 'jpeg', 'bmp'] #sxc 支持的图片文件扩展名
|
|
|
|
|
fname = u''.join(str(filename)) #sxc 处理文件名(确保 Unicode 兼容)
|
|
|
|
|
#sxc 判断文件是否为图片(检查扩展名是否在图片格式列表中)
|
|
|
|
|
isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
|
|
|
|
|
#sxc 确定文件存储根目录:图片存于 image 文件夹,其他文件存于 files 文件夹
|
|
|
|
|
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)
|
|
|
|
|
#sxc 生成唯一文件名(UUID + 原文件扩展名),避免重名
|
|
|
|
|
savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}"))
|
|
|
|
|
#sxc 防止路径遍历攻击:验证保存路径是否在基础目录内
|
|
|
|
|
if not savepath.startswith(base_dir):
|
|
|
|
|
return HttpResponse("only for post")
|
|
|
|
|
#sxc 将上传的文件分块写入本地存储
|
|
|
|
|
with open(savepath, 'wb+') as wfile:
|
|
|
|
|
for chunk in request.FILES[filename].chunks():
|
|
|
|
|
wfile.write(chunk)
|
|
|
|
|
#sxc 若为图片文件,压缩图片质量(优化存储和加载速度)
|
|
|
|
|
if isimage:
|
|
|
|
|
from PIL import Image
|
|
|
|
|
image = Image.open(savepath)
|
|
|
|
|
image.save(savepath, quality=20, optimize=True)
|
|
|
|
|
image.save(savepath, quality=20, optimize=True)#sxc 质量压缩至 20%
|
|
|
|
|
#sxc 生成文件的访问 URL 并添加到响应列表
|
|
|
|
|
url = static(savepath)
|
|
|
|
|
response.append(url)
|
|
|
|
|
return HttpResponse(response)
|
|
|
|
|
return HttpResponse(response) #sxc 返回所有上传文件的 URL 列表
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
return HttpResponse("only for post")
|
|
|
|
|
return HttpResponse("only for post") #sxc 非 POST 请求直接拒绝
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def page_not_found_view(
|
|
|
|
|
request,
|
|
|
|
|
exception,
|
|
|
|
|
template_name='blog/error_page.html'):
|
|
|
|
|
#sxc 处理 404 页面未找到错误,记录异常并返回自定义错误页面
|
|
|
|
|
if exception:
|
|
|
|
|
logger.error(exception)
|
|
|
|
|
url = request.get_full_path()
|
|
|
|
|
#sxc 渲染错误页面,传递错误信息和状态码
|
|
|
|
|
return render(request,
|
|
|
|
|
template_name,
|
|
|
|
|
{'message': _('Sorry, the page you requested is not found, please click the home page to see other?'),
|
|
|
|
|
@ -355,6 +404,7 @@ def page_not_found_view(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def server_error_view(request, template_name='blog/error_page.html'):
|
|
|
|
|
#sxc 处理 500 服务器内部错误,返回自定义错误页面
|
|
|
|
|
return render(request,
|
|
|
|
|
template_name,
|
|
|
|
|
{'message': _('Sorry, the server is busy, please click the home page to see other?'),
|
|
|
|
|
@ -366,6 +416,7 @@ def permission_denied_view(
|
|
|
|
|
request,
|
|
|
|
|
exception,
|
|
|
|
|
template_name='blog/error_page.html'):
|
|
|
|
|
#sxc 处理 403 权限拒绝错误,记录异常并返回自定义错误页面
|
|
|
|
|
if exception:
|
|
|
|
|
logger.error(exception)
|
|
|
|
|
return render(
|
|
|
|
|
@ -375,5 +426,6 @@ def permission_denied_view(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def clean_cache_view(request):
|
|
|
|
|
cache.clear()
|
|
|
|
|
#sxc 清理系统缓存的视图,调用缓存清理方法并返回成功响应
|
|
|
|
|
cache.clear() #sxc 清除所有缓存数据
|
|
|
|
|
return HttpResponse('ok')
|
|
|
|
|
|