From bc4d734627dec705bc45abeab5fe7633a0ff385f Mon Sep 17 00:00:00 2001 From: pvpncefma Date: Wed, 15 Oct 2025 21:17:18 +0800 Subject: [PATCH] ADD file via upload --- views.py | 417 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 views.py diff --git a/views.py b/views.py new file mode 100644 index 0000000..bbf8c68 --- /dev/null +++ b/views.py @@ -0,0 +1,417 @@ +import logging +import os +import uuid + +from django.conf import settings· +from django.core.paginator import Paginator +from django.http import HttpResponse, HttpResponseForbidden +from django.shortcuts import get_object_or_404 +from django.shortcuts import render +from django.templatetags.static import static +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from django.views.decorators.csrf import csrf_exempt +from django.views.generic.detail import DetailView +from django.views.generic.list import ListView +from haystack.views import SearchView +# 导入目标模型:Article(文章)、Category(分类)、Tag(标签),及其他关联模型 + +from blog.models import Article, Category, LinkShowType, Links, Tag +from comments.forms import CommentForm +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__) +# 导入目标模型:Article(文章)、Category(分类)、Tag(标签),及其他关联模型 + +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 + # 每页显示文章数量,关联Article模型的“列表展示”逻辑 + page_kwarg = 'page' + link_type = LinkShowType.L + # 省略基类中不涉及模型字段的通用方法 + def get_view_cache_key(self): + 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 + return page + + def get_queryset_cache_key(self): + """ + 瀛愮被閲嶅啓.鑾峰緱queryset鐨勭紦瀛榢ey + """ + raise NotImplementedError() + + def get_queryset_data(self): + """ + 瀛愮被閲嶅啓.鑾峰彇queryset鐨勬暟鎹? + """ + raise NotImplementedError() + + def get_queryset_from_cache(self, cache_key): + ''' + 缂撳瓨椤甸潰鏁版嵁 + :param cache_key: 缂撳瓨key + :return: + ''' + value = cache.get(cache_key) + if value: + logger.info('get view cache.key:{key}'.format(key=cache_key)) + return value + else: + article_list = self.get_queryset_data() + cache.set(cache_key, article_list) + logger.info('set view cache.key:{key}'.format(key=cache_key)) + return article_list + + def get_queryset(self): + ''' + 閲嶅啓榛樿锛屼粠缂撳瓨鑾峰彇鏁版嵁 + :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 + return super(ArticleListView, self).get_context_data(**kwargs) + # 分类详情页视图(继承文章列表基类) + +class IndexView(ArticleListView): + ''' + 棣栭〉 + ''' + # 鍙嬫儏閾炬帴绫诲瀷 + link_type = LinkShowType.I + # 从URL参数获取分类的slug(对应Category.slug字段,分类友好URL标识) + def get_queryset_data(self): + + article_list = Article.objects.filter(type='a', status='p') + return article_list + + def get_queryset_cache_key(self): + cache_key = 'index_{page}'.format(page=self.page_number) + return cache_key + # 作者详情页视图(涉及Article.author字段,此处仅作关联说明) + +class ArticleDetailView(DetailView): + ''' + 鏂囩珷璇︽儏椤甸潰 + ''' + template_name = 'blog/article_detail.html' + model = Article + pk_url_kwarg = 'article_id' + context_object_name = "article" + # 归档页不分页,显示所有文章 + def get_context_data(self, **kwargs): + # 筛选Article模型数据:status='p'(仅已发布文章),无分页返回所有数据 + # 归档逻辑依赖Article.create_time字段(文章创建时间),模板中按时间分组展示 + 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 + # Action Hook, 閫氱煡鎻掍欢"鏂囩珷璇︽儏宸茶幏鍙?" + hooks.run_action('after_article_body_get', article=article, request=self.request) + # # Filter Hook, 鍏佽鎻掍欢淇敼鏂囩珷姝f枃 + article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article, + request=self.request) + + return context + + +class CategoryDetailView(ArticleListView): + ''' + 鍒嗙被鐩綍鍒楄〃 + ''' + page_type = "鍒嗙被鐩綍褰掓。" + + def get_queryset_data(self): + # 从URL参数获取分类的slug(对应Category.slug字段,分类友好URL标识) + slug = self.kwargs['category_name'] + # 通过slug查询唯一分类:get_object_or_404表示不存在则返回404 + # 关联Category.slug字段(分类的URL唯一标识,避免中文/特殊字符问题) + category = get_object_or_404(Category, slug=slug) + + categoryname = category.name + # 获取分类名称(关联Category.name字段,分类显示名称) + self.categoryname = categoryname + # 获取当前分类的所有子分类名称:调用Category模型的自定义方法get_sub_categorys() + # 关联Category.parent字段(分类自关联,实现多级分类,如“技术→Python”) + categorynames = list( + map(lambda c: c.name, category.get_sub_categorys())) + # 筛选Article模型数据: + # category__name__in=categorynames:匹配Article.category.name字段(文章所属分类名称) + # 表示“文章所属分类在‘当前分类+子分类’列表中” + # status='p':匹配Article.status字段(仅显示已发布文章) + article_list = Article.objects.filter( + category__name__in=categorynames, status='p') + return article_list + # 省略缓存key生成、上下文添加方法... + # 作者详情页视图(涉及Article.author字段,此处仅作关联说明) + + 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) + return cache_key + + 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) + + +class AuthorDetailView(ArticleListView): + ''' + 浣滆呰鎯呴〉 + ''' + page_type = '浣滆呮枃绔犲綊妗?' + + def get_queryset_cache_key(self): + from uuslug import slugify + author_name = slugify(self.kwargs['author_name']) + # 筛选Article模型数据: + # author__username=author_name:匹配Article.author.username字段(文章作者的用户名) + # 关联Article.author字段(文章与用户的一对多关系,一篇文章对应一个作者) + # type='a'、status='p':同首页,筛选“已发布的普通文章” + 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'] + article_list = Article.objects.filter( + author__username=author_name, type='a', status='p') + return article_list + # 省略其他方法... + .0# 标签详情页视图(继承文章列表基类) + + 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) + + +class TagDetailView(ArticleListView): + ''' + 鏍囩鍒楄〃椤甸潰 + ''' + page_type = '鍒嗙被鏍囩褰掓。' + + def get_queryset_data(self): + # 从URL参数获取标签的slug(对应Tag.slug字段,标签友好URL标识) + slug = self.kwargs['tag_name'] + # 通过slug查询唯一标签:关联Tag.slug字段(标签的URL唯一标识) + tag = get_object_or_404(Tag, slug=slug) + tag_name = tag.name + # 获取标签名称(关联Tag.name字段,标签显示名称) + self.name = tag_name + # 筛选Article模型数据: + # tags__name=tag_name:匹配Article.tags.name字段(文章关联的标签名称) + # 关联Article.tags字段(文章与标签的多对多关系,一篇文章可关联多个标签,一个标签可关联多篇文章) + # type='a'、status='p':筛选“已发布的普通文章” + article_list = Article.objects.filter( + tags__name=tag_name, type='a', status='p') + return article_list + + 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) + return cache_key + # 省略缓存key生成、上下文添加方法... + + def get_context_data(self, **kwargs): + # tag_name = self.kwargs['tag_name'] + tag_name = self.name + kwargs['page_type'] = TagDetailView.page_type + kwargs['tag_name'] = tag_name + return super(TagDetailView, self).get_context_data(**kwargs) + # 文章归档页视图 + +class ArchivesView(ArticleListView): + ''' + 鏂囩珷褰掓。椤甸潰 + ''' + page_type = '鏂囩珷褰掓。' + paginate_by = None + # 归档页不分页,显示所有文章 + page_kwarg = None + template_name = 'blog/article_archives.html' + + def get_queryset_data(self): + # 筛选Article模型数据:status='p'(仅已发布文章),无分页返回所有数据 + # 归档逻辑依赖Article.create_time字段(文章创建时间),模板中按时间分组展示 + return Article.objects.filter(status='p').all() + # 省略缓存key生成方法... + # 以下为不涉及文章/分类/标签模型字段的视图(如链接列表、搜索、文件上传、错误页等),省略注释... + def get_queryset_cache_key(self): + 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): + # 省略搜索视图逻辑(若涉及文章搜索,实际关联Article.title/body字段,此处代码未直接体现)... + 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()) + + return context + + +@csrf_exempt +def fileupload(request): + # 省略文件上传逻辑... + """ + 璇ユ柟娉曢渶鑷繁鍐欒皟鐢ㄧ鏉ヤ笂浼犲浘鐗囷紝璇ユ柟娉曚粎鎻愪緵鍥惧簥鍔熻兘 + :param request: + :return: + """ + 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 = static(savepath) + response.append(url) + return HttpResponse(response) + + else: + return HttpResponse("only for post") + + +def page_not_found_view( + request, + exception, + template_name='blog/error_page.html'): + 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) + + +def server_error_view(request, template_name='blog/error_page.html'): + return render(request, + template_name, + {'message': _('Sorry, the server is busy, please click the home page to see other?'), + 'statuscode': '500'}, + status=500) + + +def permission_denied_view( + request, + exception, + template_name='blog/error_page.html'): + 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) + + +def clean_cache_view(request): + cache.clear() + return HttpResponse('ok')