|
|
|
|
@ -1,379 +1,406 @@
|
|
|
|
|
import logging
|
|
|
|
|
#zf:导入os模块用于文件操作
|
|
|
|
|
import os
|
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
#zf:从django.conf导入settings配置
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
#zf:从django.core.files.uploadedfile导入SimpleUploadedFile用于文件上传测试
|
|
|
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
|
|
|
#zf:从django.core.management导入call_command用于调用管理命令
|
|
|
|
|
from django.core.management import call_command
|
|
|
|
|
#zf:从django.core.paginator导入Paginator用于分页测试
|
|
|
|
|
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
|
|
|
|
|
#zf:从django.templatetags.static导入static用于处理静态文件
|
|
|
|
|
from django.templatetags.static import static
|
|
|
|
|
#zf:从django.test导入Client, RequestFactory, TestCase用于测试
|
|
|
|
|
from django.test import Client, RequestFactory, TestCase
|
|
|
|
|
#zf:从django.urls导入reverse用于URL反向解析
|
|
|
|
|
from django.urls import reverse
|
|
|
|
|
#zf:从django.utils导入timezone用于时区处理
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
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的缓存key
|
|
|
|
|
"""
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
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, 允许插件修改文章正文
|
|
|
|
|
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):
|
|
|
|
|
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')
|
|
|
|
|
return article_list
|
|
|
|
|
|
|
|
|
|
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'])
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
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')
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
return Article.objects.filter(status='p').all()
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
|
|
#zf:从accounts.models导入BlogUser博客用户模型
|
|
|
|
|
from accounts.models import BlogUser
|
|
|
|
|
#zf:从blog.forms导入BlogSearchForm博客搜索表单
|
|
|
|
|
from blog.forms import BlogSearchForm
|
|
|
|
|
#zf:从blog.models导入Article, Category, Tag, SideBar, Links博客相关模型
|
|
|
|
|
from blog.models import Article, Category, Tag, SideBar, Links
|
|
|
|
|
#zf:从blog.templatetags.blog_tags导入load_pagination_info, load_articletags模板标签
|
|
|
|
|
from blog.templatetags.blog_tags import load_pagination_info, load_articletags
|
|
|
|
|
#zf:从djangoblog.utils导入get_current_site, get_sha256工具函数
|
|
|
|
|
from djangoblog.utils import get_current_site, get_sha256
|
|
|
|
|
#zf:从oauth.models导入OAuthUser, OAuthConfig OAuth相关模型
|
|
|
|
|
from oauth.models import OAuthUser, OAuthConfig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#zf:创建你的测试用例
|
|
|
|
|
|
|
|
|
|
#zf:定义ArticleTest测试类,继承自TestCase
|
|
|
|
|
class ArticleTest(TestCase):
|
|
|
|
|
#zf:测试初始化方法,在每个测试方法执行前运行
|
|
|
|
|
def setUp(self):
|
|
|
|
|
#zf:创建测试客户端
|
|
|
|
|
self.client = Client()
|
|
|
|
|
#zf:创建请求工厂
|
|
|
|
|
self.factory = RequestFactory()
|
|
|
|
|
|
|
|
|
|
#zf:测试验证文章功能
|
|
|
|
|
def test_validate_article(self):
|
|
|
|
|
#zf:获取当前站点域名
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
#zf:创建或获取测试用户
|
|
|
|
|
user = BlogUser.objects.get_or_create(
|
|
|
|
|
email="liangliangyy@gmail.com",
|
|
|
|
|
username="liangliangyy")[0]
|
|
|
|
|
#zf:设置用户密码
|
|
|
|
|
user.set_password("liangliangyy")
|
|
|
|
|
#zf:设置用户为管理员
|
|
|
|
|
user.is_staff = True
|
|
|
|
|
#zf:设置用户为超级用户
|
|
|
|
|
user.is_superuser = True
|
|
|
|
|
#zf:保存用户
|
|
|
|
|
user.save()
|
|
|
|
|
#zf:测试访问用户绝对URL
|
|
|
|
|
response = self.client.get(user.get_absolute_url())
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
#zf:测试访问管理后台邮件发送日志
|
|
|
|
|
response = self.client.get('/admin/servermanager/emailsendlog/')
|
|
|
|
|
#zf:测试访问管理后台日志条目
|
|
|
|
|
response = self.client.get('admin/admin/logentry/')
|
|
|
|
|
#zf:创建侧边栏对象
|
|
|
|
|
s = SideBar()
|
|
|
|
|
#zf:设置侧边栏排序
|
|
|
|
|
s.sequence = 1
|
|
|
|
|
#zf:设置侧边栏名称
|
|
|
|
|
s.name = 'test'
|
|
|
|
|
#zf:设置侧边栏内容
|
|
|
|
|
s.content = 'test content'
|
|
|
|
|
#zf:设置侧边栏启用状态
|
|
|
|
|
s.is_enable = True
|
|
|
|
|
#zf:保存侧边栏
|
|
|
|
|
s.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建分类对象
|
|
|
|
|
category = Category()
|
|
|
|
|
#zf:设置分类名称
|
|
|
|
|
category.name = "category"
|
|
|
|
|
#zf:设置分类创建时间
|
|
|
|
|
category.creation_time = timezone.now()
|
|
|
|
|
#zf:设置分类最后修改时间
|
|
|
|
|
category.last_mod_time = timezone.now()
|
|
|
|
|
#zf:保存分类
|
|
|
|
|
category.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建标签对象
|
|
|
|
|
tag = Tag()
|
|
|
|
|
#zf:设置标签名称
|
|
|
|
|
tag.name = "nicetag"
|
|
|
|
|
#zf:保存标签
|
|
|
|
|
tag.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建文章对象
|
|
|
|
|
article = Article()
|
|
|
|
|
#zf:设置文章标题
|
|
|
|
|
article.title = "nicetitle"
|
|
|
|
|
#zf:设置文章正文
|
|
|
|
|
article.body = "nicecontent"
|
|
|
|
|
#zf:设置文章作者
|
|
|
|
|
article.author = user
|
|
|
|
|
#zf:设置文章分类
|
|
|
|
|
article.category = category
|
|
|
|
|
#zf:设置文章类型为文章
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
#zf:设置文章状态为已发布
|
|
|
|
|
article.status = 'p'
|
|
|
|
|
|
|
|
|
|
#zf:保存文章
|
|
|
|
|
article.save()
|
|
|
|
|
#zf:断言文章标签数量为0
|
|
|
|
|
self.assertEqual(0, article.tags.count())
|
|
|
|
|
#zf:给文章添加标签
|
|
|
|
|
article.tags.add(tag)
|
|
|
|
|
#zf:保存文章
|
|
|
|
|
article.save()
|
|
|
|
|
#zf:断言文章标签数量为1
|
|
|
|
|
self.assertEqual(1, article.tags.count())
|
|
|
|
|
|
|
|
|
|
#zf:循环创建20篇文章用于测试
|
|
|
|
|
for i in range(20):
|
|
|
|
|
article = Article()
|
|
|
|
|
#zf:设置文章标题
|
|
|
|
|
article.title = "nicetitle" + str(i)
|
|
|
|
|
#zf:设置文章正文
|
|
|
|
|
article.body = "nicetitle" + str(i)
|
|
|
|
|
#zf:设置文章作者
|
|
|
|
|
article.author = user
|
|
|
|
|
#zf:设置文章分类
|
|
|
|
|
article.category = category
|
|
|
|
|
#zf:设置文章类型为文章
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
#zf:设置文章状态为已发布
|
|
|
|
|
article.status = 'p'
|
|
|
|
|
#zf:保存文章
|
|
|
|
|
article.save()
|
|
|
|
|
#zf:给文章添加标签
|
|
|
|
|
article.tags.add(tag)
|
|
|
|
|
#zf:保存文章
|
|
|
|
|
article.save()
|
|
|
|
|
#zf:从blog.documents导入ELASTICSEARCH_ENABLED常量
|
|
|
|
|
from blog.documents import ELASTICSEARCH_ENABLED
|
|
|
|
|
#zf:如果启用了Elasticsearch
|
|
|
|
|
if ELASTICSEARCH_ENABLED:
|
|
|
|
|
#zf:调用build_index管理命令
|
|
|
|
|
call_command("build_index")
|
|
|
|
|
#zf:测试搜索功能
|
|
|
|
|
response = self.client.get('/search', {'q': 'nicetitle'})
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试访问文章绝对URL
|
|
|
|
|
response = self.client.get(article.get_absolute_url())
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
#zf:从djangoblog.spider_notify导入SpiderNotify用于通知搜索引擎
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
#zf:通知搜索引擎爬虫
|
|
|
|
|
SpiderNotify.notify(article.get_absolute_url())
|
|
|
|
|
#zf:测试访问标签绝对URL
|
|
|
|
|
response = self.client.get(tag.get_absolute_url())
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试访问分类绝对URL
|
|
|
|
|
response = self.client.get(category.get_absolute_url())
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试搜索功能
|
|
|
|
|
response = self.client.get('/search', {'q': 'django'})
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
#zf:加载文章标签
|
|
|
|
|
s = load_articletags(article)
|
|
|
|
|
#zf:断言结果不为None
|
|
|
|
|
self.assertIsNotNone(s)
|
|
|
|
|
|
|
|
|
|
#zf:用户登录
|
|
|
|
|
self.client.login(username='liangliangyy', password='liangliangyy')
|
|
|
|
|
|
|
|
|
|
#zf:测试访问文章归档页面
|
|
|
|
|
response = self.client.get(reverse('blog:archives'))
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:创建所有文章的分页器
|
|
|
|
|
p = Paginator(Article.objects.all(), settings.PAGINATE_BY)
|
|
|
|
|
#zf:检查分页功能
|
|
|
|
|
self.check_pagination(p, '', '')
|
|
|
|
|
|
|
|
|
|
#zf:创建按标签过滤的文章分页器
|
|
|
|
|
p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY)
|
|
|
|
|
#zf:检查标签归档分页功能
|
|
|
|
|
self.check_pagination(p, '分类标签归档', tag.slug)
|
|
|
|
|
|
|
|
|
|
#zf:创建按作者过滤的文章分页器
|
|
|
|
|
p = Paginator(
|
|
|
|
|
Article.objects.filter(
|
|
|
|
|
author__username='liangliangyy'), settings.PAGINATE_BY)
|
|
|
|
|
#zf:检查作者归档分页功能
|
|
|
|
|
self.check_pagination(p, '作者文章归档', 'liangliangyy')
|
|
|
|
|
|
|
|
|
|
#zf:创建按分类过滤的文章分页器
|
|
|
|
|
p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY)
|
|
|
|
|
#zf:检查分类归档分页功能
|
|
|
|
|
self.check_pagination(p, '分类目录归档', category.slug)
|
|
|
|
|
|
|
|
|
|
#zf:创建博客搜索表单实例
|
|
|
|
|
f = BlogSearchForm()
|
|
|
|
|
#zf:执行搜索
|
|
|
|
|
f.search()
|
|
|
|
|
#zf:从djangoblog.spider_notify导入SpiderNotify
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
#zf:通知百度搜索引擎
|
|
|
|
|
SpiderNotify.baidu_notify([article.get_full_url()])
|
|
|
|
|
|
|
|
|
|
#zf:从blog.templatetags.blog_tags导入gravatar_url, gravatar函数
|
|
|
|
|
from blog.templatetags.blog_tags import gravatar_url, gravatar
|
|
|
|
|
#zf:获取gravatar头像URL
|
|
|
|
|
u = gravatar_url('liangliangyy@gmail.com')
|
|
|
|
|
#zf:获取gravatar头像HTML
|
|
|
|
|
u = gravatar('liangliangyy@gmail.com')
|
|
|
|
|
|
|
|
|
|
#zf:创建链接对象
|
|
|
|
|
link = Links(
|
|
|
|
|
sequence=1,
|
|
|
|
|
name="lylinux",
|
|
|
|
|
link='https://wwww.lylinux.net')
|
|
|
|
|
#zf:保存链接
|
|
|
|
|
link.save()
|
|
|
|
|
#zf:测试访问链接页面
|
|
|
|
|
response = self.client.get('/links.html')
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试访问RSS订阅
|
|
|
|
|
response = self.client.get('/feed/')
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试访问站点地图
|
|
|
|
|
response = self.client.get('/sitemap.xml')
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试访问管理后台文章删除页面
|
|
|
|
|
self.client.get("/admin/blog/article/1/delete/")
|
|
|
|
|
#zf:测试访问管理后台邮件发送日志
|
|
|
|
|
self.client.get('/admin/servermanager/emailsendlog/')
|
|
|
|
|
#zf:测试访问管理后台日志条目
|
|
|
|
|
self.client.get('/admin/admin/logentry/')
|
|
|
|
|
#zf:测试访问管理后台日志条目修改页面
|
|
|
|
|
self.client.get('/admin/admin/logentry/1/change/')
|
|
|
|
|
|
|
|
|
|
#zf:检查分页功能的方法
|
|
|
|
|
def check_pagination(self, p, type, value):
|
|
|
|
|
#zf:遍历所有页面
|
|
|
|
|
for page in range(1, p.num_pages + 1):
|
|
|
|
|
#zf:加载分页信息
|
|
|
|
|
s = load_pagination_info(p.page(page), type, value)
|
|
|
|
|
#zf:断言分页信息不为None
|
|
|
|
|
self.assertIsNotNone(s)
|
|
|
|
|
#zf:如果有上一页URL
|
|
|
|
|
if s['previous_url']:
|
|
|
|
|
#zf:测试访问上一页
|
|
|
|
|
response = self.client.get(s['previous_url'])
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
#zf:如果有下一页URL
|
|
|
|
|
if s['next_url']:
|
|
|
|
|
#zf:测试访问下一页
|
|
|
|
|
response = self.client.get(s['next_url'])
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
#zf:测试图片功能
|
|
|
|
|
def test_image(self):
|
|
|
|
|
#zf:导入requests模块
|
|
|
|
|
import requests
|
|
|
|
|
#zf:获取Python官网Logo图片
|
|
|
|
|
rsp = requests.get(
|
|
|
|
|
'https://www.python.org/static/img/python-logo.png')
|
|
|
|
|
#zf:设置图片保存路径
|
|
|
|
|
imagepath = os.path.join(settings.BASE_DIR, 'python.png')
|
|
|
|
|
#zf:将图片内容写入文件
|
|
|
|
|
with open(imagepath, 'wb') as file:
|
|
|
|
|
file.write(rsp.content)
|
|
|
|
|
#zf:测试未授权上传图片
|
|
|
|
|
rsp = self.client.post('/upload')
|
|
|
|
|
#zf:断言响应状态码为403
|
|
|
|
|
self.assertEqual(rsp.status_code, 403)
|
|
|
|
|
#zf:生成上传签名
|
|
|
|
|
sign = get_sha256(get_sha256(settings.SECRET_KEY))
|
|
|
|
|
#zf:打开图片文件准备上传
|
|
|
|
|
with open(imagepath, 'rb') as file:
|
|
|
|
|
#zf:创建上传文件对象
|
|
|
|
|
imgfile = SimpleUploadedFile(
|
|
|
|
|
'python.png', file.read(), content_type='image/jpg')
|
|
|
|
|
#zf:构造表单数据
|
|
|
|
|
form_data = {'python.png': imgfile}
|
|
|
|
|
#zf:测试带签名上传图片
|
|
|
|
|
rsp = self.client.post(
|
|
|
|
|
'/upload?sign=' + sign, form_data, follow=True)
|
|
|
|
|
#zf:断言响应状态码为200
|
|
|
|
|
self.assertEqual(rsp.status_code, 200)
|
|
|
|
|
#zf:删除临时图片文件
|
|
|
|
|
os.remove(imagepath)
|
|
|
|
|
#zf:从djangoblog.utils导入save_user_avatar, send_email工具函数
|
|
|
|
|
from djangoblog.utils import save_user_avatar, send_email
|
|
|
|
|
#zf:测试发送邮件
|
|
|
|
|
send_email(['qq@qq.com'], 'testTitle', 'testContent')
|
|
|
|
|
#zf:测试保存用户头像
|
|
|
|
|
save_user_avatar(
|
|
|
|
|
'https://www.python.org/static/img/python-logo.png')
|
|
|
|
|
|
|
|
|
|
#zf:测试错误页面
|
|
|
|
|
def test_errorpage(self):
|
|
|
|
|
#zf:测试访问不存在的页面
|
|
|
|
|
rsp = self.client.get('/eee')
|
|
|
|
|
#zf:断言响应状态码为404
|
|
|
|
|
self.assertEqual(rsp.status_code, 404)
|
|
|
|
|
|
|
|
|
|
#zf:测试管理命令
|
|
|
|
|
def test_commands(self):
|
|
|
|
|
#zf:创建或获取测试用户
|
|
|
|
|
user = BlogUser.objects.get_or_create(
|
|
|
|
|
email="liangliangyy@gmail.com",
|
|
|
|
|
username="liangliangyy")[0]
|
|
|
|
|
#zf:设置用户密码
|
|
|
|
|
user.set_password("liangliangyy")
|
|
|
|
|
#zf:设置用户为管理员
|
|
|
|
|
user.is_staff = True
|
|
|
|
|
#zf:设置用户为超级用户
|
|
|
|
|
user.is_superuser = True
|
|
|
|
|
#zf:保存用户
|
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建OAuth配置对象
|
|
|
|
|
c = OAuthConfig()
|
|
|
|
|
#zf:设置OAuth类型为QQ
|
|
|
|
|
c.type = 'qq'
|
|
|
|
|
#zf:设置应用密钥
|
|
|
|
|
c.appkey = 'appkey'
|
|
|
|
|
#zf:设置应用密钥
|
|
|
|
|
c.appsecret = 'appsecret'
|
|
|
|
|
#zf:保存配置
|
|
|
|
|
c.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建OAuth用户对象
|
|
|
|
|
u = OAuthUser()
|
|
|
|
|
#zf:设置OAuth类型为QQ
|
|
|
|
|
u.type = 'qq'
|
|
|
|
|
#zf:设置openid
|
|
|
|
|
u.openid = 'openid'
|
|
|
|
|
#zf:关联博客用户
|
|
|
|
|
u.user = user
|
|
|
|
|
#zf:设置头像为静态图片
|
|
|
|
|
u.picture = static("/blog/img/avatar.png")
|
|
|
|
|
#zf:设置用户元数据
|
|
|
|
|
u.metadata = '''
|
|
|
|
|
{
|
|
|
|
|
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
|
|
|
|
|
}'''
|
|
|
|
|
#zf:保存OAuth用户
|
|
|
|
|
u.save()
|
|
|
|
|
|
|
|
|
|
#zf:创建另一个OAuth用户对象
|
|
|
|
|
u = OAuthUser()
|
|
|
|
|
#zf:设置OAuth类型为QQ
|
|
|
|
|
u.type = 'qq'
|
|
|
|
|
#zf:设置openid
|
|
|
|
|
u.openid = 'openid1'
|
|
|
|
|
#zf:设置头像URL
|
|
|
|
|
u.picture = 'https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30'
|
|
|
|
|
#zf:设置用户元数据
|
|
|
|
|
u.metadata = '''
|
|
|
|
|
{
|
|
|
|
|
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
|
|
|
|
|
}'''
|
|
|
|
|
#zf:保存OAuth用户
|
|
|
|
|
u.save()
|
|
|
|
|
|
|
|
|
|
#zf:从blog.documents导入ELASTICSEARCH_ENABLED常量
|
|
|
|
|
from blog.documents import ELASTICSEARCH_ENABLED
|
|
|
|
|
#zf:如果启用了Elasticsearch
|
|
|
|
|
if ELASTICSEARCH_ENABLED:
|
|
|
|
|
#zf:调用build_index命令构建索引
|
|
|
|
|
call_command("build_index")
|
|
|
|
|
#zf:调用ping_baidu命令通知百度
|
|
|
|
|
call_command("ping_baidu", "all")
|
|
|
|
|
#zf:调用create_testdata命令创建测试数据
|
|
|
|
|
call_command("create_testdata")
|
|
|
|
|
#zf:调用clear_cache命令清除缓存
|
|
|
|
|
call_command("clear_cache")
|
|
|
|
|
#zf:调用sync_user_avatar命令同步用户头像
|
|
|
|
|
call_command("sync_user_avatar")
|
|
|
|
|
#zf:调用build_search_words命令构建搜索词
|
|
|
|
|
call_command("build_search_words")
|