Merge pull request '1' (#9) from zhq_branch into master

1
master
pqkpsr3gu 4 months ago
commit ff9fd39bdd

@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
# Register your models here.
from .models import Article
#zhq: 文章表单类 - 可用于定制文章编辑表单
class ArticleForm(forms.ModelForm):
# body = forms.CharField(widget=AdminPagedownWidget())
@ -16,29 +16,29 @@ class ArticleForm(forms.ModelForm):
model = Article
fields = '__all__'
#zhq: Admin动作函数 - 批量发布文章
def makr_article_publish(modeladmin, request, queryset):
queryset.update(status='p')
#zhq: Admin动作函数 - 批量设为草稿
def draft_article(modeladmin, request, queryset):
queryset.update(status='d')
#zhq: Admin动作函数 - 批量关闭评论
def close_article_commentstatus(modeladmin, request, queryset):
queryset.update(comment_status='c')
#zhq: Admin动作函数 - 批量开启评论
def open_article_commentstatus(modeladmin, request, queryset):
queryset.update(comment_status='o')
#zhq: 设置动作的描述信息
makr_article_publish.short_description = _('Publish selected articles')
draft_article.short_description = _('Draft selected articles')
close_article_commentstatus.short_description = _('Close article comments')
open_article_commentstatus.short_description = _('Open article comments')
#zhq: 文章管理类 - 定制文章在Admin中的显示和行为
class ArticlelAdmin(admin.ModelAdmin):
list_per_page = 20
search_fields = ('body', 'title')
@ -62,9 +62,10 @@ class ArticlelAdmin(admin.ModelAdmin):
makr_article_publish,
draft_article,
close_article_commentstatus,
open_article_commentstatus]
open_article_commentstatus] #zhq: 注册批量动作
def link_to_category(self, obj):
# zhq: 自定义方法,生成分类的管理后台链接
info = (obj.category._meta.app_label, obj.category._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,))
return format_html(u'<a href="%s">%s</a>' % (link, obj.category.name))
@ -72,6 +73,7 @@ class ArticlelAdmin(admin.ModelAdmin):
link_to_category.short_description = _('category')
def get_form(self, request, obj=None, **kwargs):
# zhq: 限制作者字段只能选择超级用户
form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['author'].queryset = get_user_model(
).objects.filter(is_superuser=True)
@ -81,6 +83,7 @@ class ArticlelAdmin(admin.ModelAdmin):
super(ArticlelAdmin, self).save_model(request, obj, form, change)
def get_view_on_site_url(self, obj=None):
# zhq: 获取文章在前台的访问链接
if obj:
url = obj.get_full_url()
return url
@ -89,24 +92,24 @@ class ArticlelAdmin(admin.ModelAdmin):
site = get_current_site().domain
return site
#zhq: 标签管理类 - 简化管理界面
class TagAdmin(admin.ModelAdmin):
exclude = ('slug', 'last_mod_time', 'creation_time')
#zhq: 分类管理类 - 显示父级分类信息
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'parent_category', 'index')
exclude = ('slug', 'last_mod_time', 'creation_time')
#zhq: 友情链接管理类
class LinksAdmin(admin.ModelAdmin):
exclude = ('last_mod_time', 'creation_time')
#zhq: 侧边栏管理类
class SideBarAdmin(admin.ModelAdmin):
list_display = ('name', 'content', 'is_enable', 'sequence')
exclude = ('last_mod_time', 'creation_time')
#zhq: 博客设置管理类 - 使用默认管理界面
class BlogSettingsAdmin(admin.ModelAdmin):
pass

@ -1,5 +1,5 @@
from django.apps import AppConfig
#zhq: 博客应用配置类 - 继承自Django的AppConfig基类
class BlogConfig(AppConfig):
name = 'blog'
name = 'blog' #zhq: 应用名称对应INSTALLED_APPS中的名称

@ -7,7 +7,7 @@ from .models import Category, Article
logger = logging.getLogger(__name__)
#zhq: SEO上下文处理器 - 为所有模板提供SEO相关变量和导航数据
def seo_processor(requests):
key = 'seo_processor'
value = cache.get(key)
@ -15,29 +15,32 @@ def seo_processor(requests):
return value
else:
logger.info('set processor cache.')
# zhq: 获取博客全局设置
setting = get_blog_setting()
# zhq: 构建上下文数据字典
value = {
'SITE_NAME': setting.site_name,
'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,
'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,
'SITE_SEO_DESCRIPTION': setting.site_seo_description,
'SITE_DESCRIPTION': setting.site_description,
'SITE_KEYWORDS': setting.site_keywords,
'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',
'ARTICLE_SUB_LENGTH': setting.article_sub_length,
'nav_category_list': Category.objects.all(),
'SITE_NAME': setting.site_name,#zhq: 网站名称
'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,#zhq: 是否显示Google广告
'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,#zhq: Google广告代码
'SITE_SEO_DESCRIPTION': setting.site_seo_description,#zhq: SEO描述
'SITE_DESCRIPTION': setting.site_description,#zhq: 网站描述
'SITE_KEYWORDS': setting.site_keywords,#zhq: 网站关键词
'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',#zhq: 网站基础URL
'ARTICLE_SUB_LENGTH': setting.article_sub_length,#zhq: 文章摘要长度
'nav_category_list': Category.objects.all(),#zhq: 导航分类列表
'nav_pages': Article.objects.filter(
type='p',
status='p'),
'OPEN_SITE_COMMENT': setting.open_site_comment,
'BEIAN_CODE': setting.beian_code,
'ANALYTICS_CODE': setting.analytics_code,
"BEIAN_CODE_GONGAN": setting.gongan_beiancode,
"SHOW_GONGAN_CODE": setting.show_gongan_code,
"CURRENT_YEAR": timezone.now().year,
"GLOBAL_HEADER": setting.global_header,
"GLOBAL_FOOTER": setting.global_footer,
"COMMENT_NEED_REVIEW": setting.comment_need_review,
type='p',#zhq: 页面类型
status='p'),#zhq: 发布状态
'OPEN_SITE_COMMENT': setting.open_site_comment,#zhq: 是否开启全站评论
'BEIAN_CODE': setting.beian_code,#zhq: ICP备案号
'ANALYTICS_CODE': setting.analytics_code,#zhq: 网站统计代码
"BEIAN_CODE_GONGAN": setting.gongan_beiancode,#zhq: 公安备案号
"SHOW_GONGAN_CODE": setting.show_gongan_code,#zhq: 是否显示公安备案号
"CURRENT_YEAR": timezone.now().year,#zhq: 当前年份
"GLOBAL_HEADER": setting.global_header,#zhq: 全局头部HTML
"GLOBAL_FOOTER": setting.global_footer,#zhq: 全局尾部HTML
"COMMENT_NEED_REVIEW": setting.comment_need_review,#zhq: 评论是否需要审核
}
# zhq: 缓存上下文数据10小时提高性能
cache.set(key, value, 60 * 60 * 10)
return value

@ -5,15 +5,18 @@ from haystack.forms import SearchForm
logger = logging.getLogger(__name__)
#zhq: 博客搜索表单类 - 继承自Haystack的SearchForm
class BlogSearchForm(SearchForm):
querydata = forms.CharField(required=True)
def search(self):
#zhq: 执行搜索操作调用父类的search方法
datas = super(BlogSearchForm, self).search()
# zhq: 如果表单验证失败,返回空结果
if not self.is_valid():
return self.no_query_found()
# zhq: 记录搜索关键词到日志
if self.cleaned_data['querydata']:
logger.info(self.cleaned_data['querydata'])
return datas

@ -9,6 +9,7 @@ from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager
logger = logging.getLogger(__name__)
#zhq: 在线中间件类 - 用于监控页面加载时间和用户访问信息
class OnlineMiddleware(object):
def __init__(self, get_response=None):
self.get_response = get_response
@ -16,16 +17,20 @@ class OnlineMiddleware(object):
def __call__(self, request):
''' page render time '''
# zhq: 记录请求开始时间
start_time = time.time()
response = self.get_response(request)
#zhq: 获取客户端IP地址和User-Agent信息
http_user_agent = request.META.get('HTTP_USER_AGENT', '')
ip, _ = get_client_ip(request)
user_agent = parse(http_user_agent)
if not response.streaming:
try:
#zhq: 计算页面渲染时间
cast_time = time.time() - start_time
# zhq: 如果启用了Elasticsearch记录性能数据
if ELASTICSEARCH_ENABLED:
time_taken = round((cast_time) * 1000, 2)
time_taken = round((cast_time) * 1000, 2) #zhq: 转换为毫秒并保留2位小数
url = request.path
from django.utils import timezone
ElaspedTimeDocumentManager.create(
@ -34,8 +39,9 @@ class OnlineMiddleware(object):
log_datetime=timezone.now(),
useragent=user_agent,
ip=ip)
# zhq: 在响应内容中替换加载时间占位符
response.content = response.content.replace(
b'<!!LOAD_TIMES!!>', str.encode(str(cast_time)[:5]))
b'<!!LOAD_TIMES!!>', str.encode(str(cast_time)[:5])) #zhq: 保留前5位字符
except Exception as e:
logger.error("Error OnlineMiddleware: %s" % e)

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

@ -3,58 +3,71 @@ from django.views.decorators.cache import cache_page
from . import views
app_name = "blog"
app_name = "blog" #zhq: 应用命名空间用于URL反向解析
urlpatterns = [
#zhq: 首页路由 - 显示文章列表
path(
r'',
views.IndexView.as_view(),
name='index'),
#zhq: 首页分页路由 - 支持分页浏览
path(
r'page/<int:page>/',
views.IndexView.as_view(),
name='index_page'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'article/<int:year>/<int:month>/<int:day>/<int:article_id>.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'category/<slug:category_name>.html',
views.CategoryDetailView.as_view(),
name='category_detail'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'category/<slug:category_name>/<int:page>.html',
views.CategoryDetailView.as_view(),
name='category_detail_page'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'author/<author_name>.html',
views.AuthorDetailView.as_view(),
name='author_detail'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'author/<author_name>/<int:page>.html',
views.AuthorDetailView.as_view(),
name='author_detail_page'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'tag/<slug:tag_name>.html',
views.TagDetailView.as_view(),
name='tag_detail'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
r'tag/<slug:tag_name>/<int:page>.html',
views.TagDetailView.as_view(),
name='tag_detail_page'),
#zhq: 文章详情页路由 - 包含年月日信息用于SEO优化
path(
'archives.html',
cache_page(
60 * 60)(
views.ArchivesView.as_view()),
name='archives'),
#zhq: 友情链接页路由 - 显示所有启用的友情链接
path(
'links.html',
views.LinkListView.as_view(),
name='links'),
#zhq: 友情链接页路由 - 显示所有启用的友情链接
path(
r'upload',
views.fileupload,
name='upload'),
# zhq: 缓存清理路由 - 手动清理系统缓存
path(
r'clean',
views.clean_cache_view,

@ -23,7 +23,7 @@ from djangoblog.utils import cache, get_blog_setting, get_sha256
logger = logging.getLogger(__name__)
#zhq: 文章列表基类视图 - 提供通用的列表功能和缓存机制
class ArticleListView(ListView):
# template_name属性用于指定使用哪个模板进行渲染
template_name = 'blog/article_index.html'
@ -33,7 +33,7 @@ class ArticleListView(ListView):
# 页面类型,分类目录或标签列表等
page_type = ''
paginate_by = settings.PAGINATE_BY
paginate_by = settings.PAGINATE_BY #zhq: 从配置获取分页大小
page_kwarg = 'page'
link_type = LinkShowType.L
@ -42,6 +42,7 @@ class ArticleListView(ListView):
@property
def page_number(self):
# zhq: 获取当前页码支持URL参数和GET参数
page_kwarg = self.page_kwarg
page = self.kwargs.get(
page_kwarg) or self.request.GET.get(page_kwarg) or 1
@ -88,7 +89,7 @@ class ArticleListView(ListView):
kwargs['linktype'] = self.link_type
return super(ArticleListView, self).get_context_data(**kwargs)
#zhq: 获取当前页码支持URL参数和GET参数
class IndexView(ArticleListView):
'''
首页
@ -97,6 +98,7 @@ class IndexView(ArticleListView):
link_type = LinkShowType.I
def get_queryset_data(self):
# zhq: 获取已发布的普通文章
article_list = Article.objects.filter(type='a', status='p')
return article_list
@ -104,7 +106,7 @@ class IndexView(ArticleListView):
cache_key = 'index_{page}'.format(page=self.page_number)
return cache_key
#zhq: 文章详情页视图 - 显示单篇文章内容和评论
class ArticleDetailView(DetailView):
'''
文章详情页面
@ -117,6 +119,7 @@ class ArticleDetailView(DetailView):
def get_context_data(self, **kwargs):
comment_form = CommentForm()
# zhq: 获取文章评论并进行分页处理
article_comments = self.object.comment_list()
parent_comments = article_comments.filter(parent_comment=None)
blog_setting = get_blog_setting()
@ -135,6 +138,7 @@ class ArticleDetailView(DetailView):
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
# zhq: 获取文章评论并进行分页处理
if next_page:
kwargs[
'comment_next_page_url'] = self.object.get_absolute_url() + f'?comment_page={next_page}#commentlist-container'
@ -160,7 +164,7 @@ class ArticleDetailView(DetailView):
return context
#zhq: 分类详情页视图 - 显示指定分类下的文章
class CategoryDetailView(ArticleListView):
'''
分类目录列表
@ -173,6 +177,7 @@ class CategoryDetailView(ArticleListView):
categoryname = category.name
self.categoryname = categoryname
# zhq: 获取分类及其所有子分类
categorynames = list(
map(lambda c: c.name, category.get_sub_categorys()))
article_list = Article.objects.filter(
@ -199,7 +204,7 @@ class CategoryDetailView(ArticleListView):
kwargs['tag_name'] = categoryname
return super(CategoryDetailView, self).get_context_data(**kwargs)
#zhq: 作者详情页视图 - 显示指定作者的文章
class AuthorDetailView(ArticleListView):
'''
作者详情页
@ -225,7 +230,7 @@ class AuthorDetailView(ArticleListView):
kwargs['tag_name'] = author_name
return super(AuthorDetailView, self).get_context_data(**kwargs)
#zhq: 标签详情页视图 - 显示指定标签的文章
class TagDetailView(ArticleListView):
'''
标签列表页面
@ -257,7 +262,7 @@ class TagDetailView(ArticleListView):
kwargs['tag_name'] = tag_name
return super(TagDetailView, self).get_context_data(**kwargs)
#zhq: 文章归档页视图 - 显示所有文章的按时间归档
class ArchivesView(ArticleListView):
'''
文章归档页面
@ -274,7 +279,7 @@ class ArchivesView(ArticleListView):
cache_key = 'archives'
return cache_key
#zhq: 友情链接页视图
class LinkListView(ListView):
model = Links
template_name = 'blog/links_list.html'
@ -282,7 +287,7 @@ class LinkListView(ListView):
def get_queryset(self):
return Links.objects.filter(is_enable=True)
#zhq: Elasticsearch搜索视图
class EsSearchView(SearchView):
def get_context(self):
paginator, page = self.build_page()
@ -299,7 +304,7 @@ class EsSearchView(SearchView):
return context
#zhq: 文件上传视图 - 支持图片和其他文件上传
@csrf_exempt
def fileupload(request):
"""
@ -308,6 +313,7 @@ def fileupload(request):
:return:
"""
if request.method == 'POST':
# zhq: 验证上传签名,确保安全性
sign = request.GET.get('sign', None)
if not sign:
return HttpResponseForbidden()
@ -322,6 +328,7 @@ def fileupload(request):
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)
# zhq: 使用UUID生成唯一文件名避免冲突
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")
@ -329,6 +336,7 @@ def fileupload(request):
for chunk in request.FILES[filename].chunks():
wfile.write(chunk)
if isimage:
# zhq: 对图片进行压缩优化
from PIL import Image
image = Image.open(savepath)
image.save(savepath, quality=20, optimize=True)
@ -339,7 +347,7 @@ def fileupload(request):
else:
return HttpResponse("only for post")
#zhq: 404错误页面视图
def page_not_found_view(
request,
exception,
@ -353,7 +361,7 @@ def page_not_found_view(
'statuscode': '404'},
status=404)
#zhq: 500服务器错误页面视图
def server_error_view(request, template_name='blog/error_page.html'):
return render(request,
template_name,
@ -361,7 +369,7 @@ def server_error_view(request, template_name='blog/error_page.html'):
'statuscode': '500'},
status=500)
#zhq: 403权限拒绝页面视图
def permission_denied_view(
request,
exception,
@ -373,7 +381,7 @@ def permission_denied_view(
'message': _('Sorry, you do not have permission to access this page?'),
'statuscode': '403'}, status=403)
zhq: 清理缓存视图
def clean_cache_view(request):
cache.clear()
return HttpResponse('ok')

Loading…
Cancel
Save