|
|
|
|
@ -25,49 +25,56 @@ register = template.Library()
|
|
|
|
|
|
|
|
|
|
@register.simple_tag(takes_context=True)
|
|
|
|
|
def head_meta(context):
|
|
|
|
|
"""渲染页面头部SEO相关meta标签(通过插件钩子扩展)"""
|
|
|
|
|
return mark_safe(hooks.apply_filters('head_meta', '', context))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.simple_tag
|
|
|
|
|
def timeformat(data):
|
|
|
|
|
"""格式化时间(使用项目配置的时间格式)"""
|
|
|
|
|
try:
|
|
|
|
|
return data.strftime(settings.TIME_FORMAT)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error(f"时间格式化失败: {e}")
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.simple_tag
|
|
|
|
|
def datetimeformat(data):
|
|
|
|
|
"""格式化日期时间(使用项目配置的日期时间格式)"""
|
|
|
|
|
try:
|
|
|
|
|
return data.strftime(settings.DATE_TIME_FORMAT)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(e)
|
|
|
|
|
logger.error(f"日期时间格式化失败: {e}")
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.filter()
|
|
|
|
|
@stringfilter
|
|
|
|
|
def custom_markdown(content):
|
|
|
|
|
"""将Markdown内容转换为HTML(用于文章正文)"""
|
|
|
|
|
return mark_safe(CommonMarkdown.get_markdown(content))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.simple_tag
|
|
|
|
|
def get_markdown_toc(content):
|
|
|
|
|
body, toc = CommonMarkdown.get_markdown_with_toc(content)
|
|
|
|
|
"""获取Markdown内容的目录结构"""
|
|
|
|
|
_, toc = CommonMarkdown.get_markdown_with_toc(content)
|
|
|
|
|
return mark_safe(toc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.filter()
|
|
|
|
|
@stringfilter
|
|
|
|
|
def comment_markdown(content):
|
|
|
|
|
content = CommonMarkdown.get_markdown(content)
|
|
|
|
|
return mark_safe(sanitize_html(content))
|
|
|
|
|
"""将评论内容的Markdown转换为安全HTML(过滤危险标签)"""
|
|
|
|
|
markdown_html = CommonMarkdown.get_markdown(content)
|
|
|
|
|
return mark_safe(sanitize_html(markdown_html))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.filter(is_safe=True)
|
|
|
|
|
@stringfilter
|
|
|
|
|
def truncatechars_content(content):
|
|
|
|
|
"""按配置长度截断HTML内容(保留标签结构)"""
|
|
|
|
|
from django.template.defaultfilters import truncatechars_html
|
|
|
|
|
from djangoblog.utils import get_blog_setting
|
|
|
|
|
blog_setting = get_blog_setting()
|
|
|
|
|
@ -77,153 +84,196 @@ def truncatechars_content(content):
|
|
|
|
|
@register.filter(is_safe=True)
|
|
|
|
|
@stringfilter
|
|
|
|
|
def truncate(content):
|
|
|
|
|
"""截断纯文本内容(去除HTML标签后保留前150字符)"""
|
|
|
|
|
from django.utils.html import strip_tags
|
|
|
|
|
return strip_tags(content)[:150]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/breadcrumb.html')
|
|
|
|
|
def load_breadcrumb(article):
|
|
|
|
|
names = article.get_category_tree()
|
|
|
|
|
"""加载文章面包屑导航"""
|
|
|
|
|
category_tree = article.get_category_tree()
|
|
|
|
|
from djangoblog.utils import get_blog_setting
|
|
|
|
|
blog_setting = get_blog_setting()
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
names.append((blog_setting.site_name, '/'))
|
|
|
|
|
names = names[::-1]
|
|
|
|
|
# 拼接面包屑:首页 -> 分类 -> 文章
|
|
|
|
|
breadcrumb_names = category_tree
|
|
|
|
|
breadcrumb_names.append((blog_setting.site_name, '/'))
|
|
|
|
|
breadcrumb_names = breadcrumb_names[::-1]
|
|
|
|
|
return {
|
|
|
|
|
'names': names,
|
|
|
|
|
'names': breadcrumb_names,
|
|
|
|
|
'title': article.title,
|
|
|
|
|
'count': len(names) + 1
|
|
|
|
|
'count': len(breadcrumb_names) + 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/article_tag_list.html')
|
|
|
|
|
def load_articletags(article):
|
|
|
|
|
tags = article.tags.all()
|
|
|
|
|
tags_list = []
|
|
|
|
|
for tag in tags:
|
|
|
|
|
url = tag.get_absolute_url()
|
|
|
|
|
count = tag.get_article_count()
|
|
|
|
|
tags_list.append((
|
|
|
|
|
url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES)
|
|
|
|
|
))
|
|
|
|
|
return {'article_tags_list': tags_list}
|
|
|
|
|
"""加载文章标签列表(带随机颜色)"""
|
|
|
|
|
article_tags = article.tags.all()
|
|
|
|
|
tag_list = []
|
|
|
|
|
for tag in article_tags:
|
|
|
|
|
tag_url = tag.get_absolute_url()
|
|
|
|
|
tag_article_count = tag.get_article_count()
|
|
|
|
|
tag_color = random.choice(settings.BOOTSTRAP_COLOR_TYPES)
|
|
|
|
|
tag_list.append((tag_url, tag_article_count, tag, tag_color))
|
|
|
|
|
return {'article_tags_list': tag_list}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/sidebar.html')
|
|
|
|
|
def load_sidebar(user, linktype):
|
|
|
|
|
"""加载侧边栏内容(带缓存,按链接类型过滤)"""
|
|
|
|
|
cache_key = f"sidebar{linktype}"
|
|
|
|
|
cached_value = cache.get(cache_key)
|
|
|
|
|
if cached_value:
|
|
|
|
|
cached_value['user'] = user
|
|
|
|
|
return cached_value
|
|
|
|
|
else:
|
|
|
|
|
logger.info('load sidebar')
|
|
|
|
|
from djangoblog.utils import get_blog_setting
|
|
|
|
|
blog_setting = get_blog_setting()
|
|
|
|
|
recent_articles = Article.objects.filter(
|
|
|
|
|
status='p')[:blog_setting.sidebar_article_count]
|
|
|
|
|
sidebar_categories = Category.objects.all() # 修复:命名更清晰
|
|
|
|
|
extra_sidebars = SideBar.objects.filter(
|
|
|
|
|
is_enable=True).order_by('sequence')
|
|
|
|
|
most_read_articles = Article.objects.filter(status='p').order_by(
|
|
|
|
|
'-views')[:blog_setting.sidebar_article_count]
|
|
|
|
|
dates = Article.objects.datetimes('creation_time', 'month', order='DESC')
|
|
|
|
|
links = Links.objects.filter(is_enable=True).filter(
|
|
|
|
|
Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A))
|
|
|
|
|
comment_list = Comment.objects.filter(is_enable=True).order_by( # 修复:命名更清晰
|
|
|
|
|
'-id')[:blog_setting.sidebar_comment_count]
|
|
|
|
|
|
|
|
|
|
# 标签云逻辑:使用更规范的空序列判断
|
|
|
|
|
increment = 5
|
|
|
|
|
tags = Tag.objects.all()
|
|
|
|
|
sidebar_tags = None
|
|
|
|
|
if tags: # 修复:直接用if判断序列是否为空(G.TYP.04)
|
|
|
|
|
# 过滤出有文章数量的标签
|
|
|
|
|
valid_tag_counts = [ # 修复:命名更清晰
|
|
|
|
|
(tag, tag.get_article_count())
|
|
|
|
|
for tag in tags
|
|
|
|
|
if tag.get_article_count() > 0
|
|
|
|
|
]
|
|
|
|
|
total_article_count = sum( # 修复:命名更清晰
|
|
|
|
|
tag_count[1] for tag_count in valid_tag_counts
|
|
|
|
|
)
|
|
|
|
|
# 计算平均值用于字体大小缩放
|
|
|
|
|
avg_article_count = 1 # 修复:命名更清晰
|
|
|
|
|
if total_article_count > 0 and len(valid_tag_counts) > 0:
|
|
|
|
|
avg_article_count = total_article_count / len(valid_tag_counts)
|
|
|
|
|
|
|
|
|
|
# 生成标签云数据
|
|
|
|
|
sidebar_tags = [ # 修复:使用列表推导更清晰
|
|
|
|
|
(tag, count, (count / avg_article_count) * increment + 10)
|
|
|
|
|
for tag, count in valid_tag_counts
|
|
|
|
|
]
|
|
|
|
|
random.shuffle(sidebar_tags)
|
|
|
|
|
|
|
|
|
|
result = { # 修复:命名更清晰
|
|
|
|
|
'recent_articles': recent_articles,
|
|
|
|
|
'sidebar_categories': sidebar_categories,
|
|
|
|
|
'most_read_articles': most_read_articles,
|
|
|
|
|
'article_dates': dates,
|
|
|
|
|
'sidebar_comments': comment_list,
|
|
|
|
|
'sidabar_links': links,
|
|
|
|
|
'show_google_adsense': blog_setting.show_google_adsense,
|
|
|
|
|
'google_adsense_codes': blog_setting.google_adsense_codes,
|
|
|
|
|
'open_site_comment': blog_setting.open_site_comment,
|
|
|
|
|
'show_gongan_code': blog_setting.show_gongan_code,
|
|
|
|
|
'sidebar_tags': sidebar_tags,
|
|
|
|
|
'extra_sidebars': extra_sidebars
|
|
|
|
|
}
|
|
|
|
|
cache.set(cache_key, result, 60 * 60 * 60 * 3)
|
|
|
|
|
logger.info(f'set sidebar cache.key: {cache_key}')
|
|
|
|
|
result['user'] = user
|
|
|
|
|
return result
|
|
|
|
|
cached_sidebar = cache.get(cache_key)
|
|
|
|
|
|
|
|
|
|
if cached_sidebar:
|
|
|
|
|
cached_sidebar['user'] = user
|
|
|
|
|
return cached_sidebar
|
|
|
|
|
|
|
|
|
|
logger.info(f'缓存未命中,加载侧边栏内容(linktype: {linktype})')
|
|
|
|
|
from djangoblog.utils import get_blog_setting
|
|
|
|
|
blog_setting = get_blog_setting()
|
|
|
|
|
|
|
|
|
|
# 侧边栏核心数据查询
|
|
|
|
|
recent_articles = Article.objects.filter(status='p')[:blog_setting.sidebar_article_count]
|
|
|
|
|
sidebar_categories = Category.objects.all()
|
|
|
|
|
extra_sidebars = SideBar.objects.filter(is_enable=True).order_by('sequence')
|
|
|
|
|
most_read_articles = Article.objects.filter(status='p').order_by('-views')[:blog_setting.sidebar_article_count]
|
|
|
|
|
article_dates = Article.objects.datetimes('creation_time', 'month', order='DESC')
|
|
|
|
|
valid_links = Links.objects.filter(
|
|
|
|
|
is_enable=True
|
|
|
|
|
).filter(Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A))
|
|
|
|
|
recent_comments = Comment.objects.filter(is_enable=True).order_by('-id')[:blog_setting.sidebar_comment_count]
|
|
|
|
|
|
|
|
|
|
# 标签云生成逻辑
|
|
|
|
|
all_tags = Tag.objects.all()
|
|
|
|
|
sidebar_tags = None
|
|
|
|
|
if all_tags:
|
|
|
|
|
# 过滤有文章的有效标签
|
|
|
|
|
valid_tags_with_count = [
|
|
|
|
|
(tag, tag.get_article_count())
|
|
|
|
|
for tag in all_tags
|
|
|
|
|
if tag.get_article_count() > 0
|
|
|
|
|
]
|
|
|
|
|
total_article_num = sum(tag_count[1] for tag_count in valid_tags_with_count)
|
|
|
|
|
avg_article_num = 1 # 避免除零错误
|
|
|
|
|
|
|
|
|
|
if total_article_num > 0 and len(valid_tags_with_count) > 0:
|
|
|
|
|
avg_article_num = total_article_num / len(valid_tags_with_count)
|
|
|
|
|
|
|
|
|
|
# 计算标签字体大小(基于文章数量占比)
|
|
|
|
|
sidebar_tags = [
|
|
|
|
|
(tag, count, (count / avg_article_num) * 5 + 10)
|
|
|
|
|
for tag, count in valid_tags_with_count
|
|
|
|
|
]
|
|
|
|
|
random.shuffle(sidebar_tags)
|
|
|
|
|
|
|
|
|
|
# 侧边栏数据组装
|
|
|
|
|
sidebar_data = {
|
|
|
|
|
'recent_articles': recent_articles,
|
|
|
|
|
'sidebar_categories': sidebar_categories,
|
|
|
|
|
'most_read_articles': most_read_articles,
|
|
|
|
|
'article_dates': article_dates,
|
|
|
|
|
'sidebar_comments': recent_comments,
|
|
|
|
|
'sidabar_links': valid_links,
|
|
|
|
|
'show_google_adsense': blog_setting.show_google_adsense,
|
|
|
|
|
'google_adsense_codes': blog_setting.google_adsense_codes,
|
|
|
|
|
'open_site_comment': blog_setting.open_site_comment,
|
|
|
|
|
'show_gongan_code': blog_setting.show_gongan_code,
|
|
|
|
|
'sidebar_tags': sidebar_tags,
|
|
|
|
|
'extra_sidebars': extra_sidebars
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 缓存3小时
|
|
|
|
|
cache.set(cache_key, sidebar_data, 60 * 60 * 3)
|
|
|
|
|
logger.info(f'侧边栏缓存设置成功(key: {cache_key})')
|
|
|
|
|
sidebar_data['user'] = user
|
|
|
|
|
return sidebar_data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/article_meta_info.html')
|
|
|
|
|
def load_article_metas(article, user):
|
|
|
|
|
"""加载文章元信息(作者、发布时间、分类等)"""
|
|
|
|
|
return {'article': article, 'user': user}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/article_pagination.html')
|
|
|
|
|
def load_pagination_info(page_obj, page_type, tag_name):
|
|
|
|
|
"""加载分页信息(根据页面类型生成上一页/下一页链接)"""
|
|
|
|
|
previous_url = ''
|
|
|
|
|
next_url = ''
|
|
|
|
|
|
|
|
|
|
# 首页分页
|
|
|
|
|
if page_type == '':
|
|
|
|
|
if page_obj.has_next():
|
|
|
|
|
next_number = page_obj.next_page_number()
|
|
|
|
|
next_url = reverse('blog:index_page', kwargs={'page': next_number})
|
|
|
|
|
next_url = reverse(
|
|
|
|
|
'blog:index_page',
|
|
|
|
|
kwargs={'page': page_obj.next_page_number()}
|
|
|
|
|
)
|
|
|
|
|
if page_obj.has_previous():
|
|
|
|
|
previous_number = page_obj.previous_page_number()
|
|
|
|
|
previous_url = reverse('blog:index_page', kwargs={'page': previous_number})
|
|
|
|
|
if page_type == '分类标签归档':
|
|
|
|
|
previous_url = reverse(
|
|
|
|
|
'blog:index_page',
|
|
|
|
|
kwargs={'page': page_obj.previous_page_number()}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 标签归档分页
|
|
|
|
|
elif page_type == '分类标签归档':
|
|
|
|
|
tag = get_object_or_404(Tag, name=tag_name)
|
|
|
|
|
if page_obj.has_next():
|
|
|
|
|
next_number = page_obj.next_page_number()
|
|
|
|
|
next_url = reverse('blog:tag_detail_page',
|
|
|
|
|
kwargs={'page': next_number, 'tag_name': tag.slug})
|
|
|
|
|
next_url = reverse(
|
|
|
|
|
'blog:tag_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.next_page_number(),
|
|
|
|
|
'tag_name': tag.slug
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
if page_obj.has_previous():
|
|
|
|
|
previous_number = page_obj.previous_page_number()
|
|
|
|
|
previous_url = reverse('blog:tag_detail_page',
|
|
|
|
|
kwargs={'page': previous_number, 'tag_name': tag.slug})
|
|
|
|
|
if page_type == '作者文章归档':
|
|
|
|
|
previous_url = reverse(
|
|
|
|
|
'blog:tag_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.previous_page_number(),
|
|
|
|
|
'tag_name': tag.slug
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 作者归档分页
|
|
|
|
|
elif page_type == '作者文章归档':
|
|
|
|
|
if page_obj.has_next():
|
|
|
|
|
next_number = page_obj.next_page_number()
|
|
|
|
|
next_url = reverse('blog:author_detail_page',
|
|
|
|
|
kwargs={'page': next_number, 'author_name': tag_name})
|
|
|
|
|
next_url = reverse(
|
|
|
|
|
'blog:author_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.next_page_number(),
|
|
|
|
|
'author_name': tag_name
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
if page_obj.has_previous():
|
|
|
|
|
previous_number = page_obj.previous_page_number()
|
|
|
|
|
previous_url = reverse('blog:author_detail_page',
|
|
|
|
|
kwargs={'page': previous_number, 'author_name': tag_name})
|
|
|
|
|
if page_type == '分类目录归档':
|
|
|
|
|
previous_url = reverse(
|
|
|
|
|
'blog:author_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.previous_page_number(),
|
|
|
|
|
'author_name': tag_name
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 分类归档分页
|
|
|
|
|
elif page_type == '分类目录归档':
|
|
|
|
|
category = get_object_or_404(Category, name=tag_name)
|
|
|
|
|
if page_obj.has_next():
|
|
|
|
|
next_number = page_obj.next_page_number()
|
|
|
|
|
next_url = reverse('blog:category_detail_page',
|
|
|
|
|
kwargs={'page': next_number, 'category_name': category.slug})
|
|
|
|
|
next_url = reverse(
|
|
|
|
|
'blog:category_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.next_page_number(),
|
|
|
|
|
'category_name': category.slug
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
if page_obj.has_previous():
|
|
|
|
|
previous_number = page_obj.previous_page_number()
|
|
|
|
|
previous_url = reverse('blog:category_detail_page',
|
|
|
|
|
kwargs={'page': previous_number, 'category_name': category.slug})
|
|
|
|
|
previous_url = reverse(
|
|
|
|
|
'blog:category_detail_page',
|
|
|
|
|
kwargs={
|
|
|
|
|
'page': page_obj.previous_page_number(),
|
|
|
|
|
'category_name': category.slug
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'previous_url': previous_url,
|
|
|
|
|
'next_url': next_url,
|
|
|
|
|
@ -233,6 +283,7 @@ def load_pagination_info(page_obj, page_type, tag_name):
|
|
|
|
|
|
|
|
|
|
@register.inclusion_tag('blog/tags/article_info.html')
|
|
|
|
|
def load_article_detail(article, isindex, user):
|
|
|
|
|
"""加载文章详情页内容(根据是否为首页调整显示逻辑)"""
|
|
|
|
|
from djangoblog.utils import get_blog_setting
|
|
|
|
|
blog_setting = get_blog_setting()
|
|
|
|
|
return {
|
|
|
|
|
@ -245,46 +296,53 @@ def load_article_detail(article, isindex, user):
|
|
|
|
|
|
|
|
|
|
@register.filter
|
|
|
|
|
def gravatar_url(email, size=40):
|
|
|
|
|
cache_key = f'gravatar/{email}' # 修复:命名更清晰
|
|
|
|
|
cached_url = cache.get(cache_key)
|
|
|
|
|
if cached_url:
|
|
|
|
|
return cached_url
|
|
|
|
|
else:
|
|
|
|
|
oauth_users = OAuthUser.objects.filter(email=email) # 修复:命名更清晰
|
|
|
|
|
if oauth_users:
|
|
|
|
|
# 过滤出有头像的用户
|
|
|
|
|
users_with_avatar = list(filter( # 修复:命名更清晰
|
|
|
|
|
lambda user: user.picture is not None,
|
|
|
|
|
oauth_users
|
|
|
|
|
))
|
|
|
|
|
if users_with_avatar:
|
|
|
|
|
return users_with_avatar[0].picture
|
|
|
|
|
|
|
|
|
|
# 生成Gravatar链接
|
|
|
|
|
email_encoded = email.encode('utf-8') # 修复:命名更清晰
|
|
|
|
|
default_avatar = static('blog/img/avatar.png') # 修复:命名更清晰
|
|
|
|
|
gravatar_params = urllib.parse.urlencode({ # 修复:命名更清晰
|
|
|
|
|
'd': default_avatar,
|
|
|
|
|
's': str(size)
|
|
|
|
|
})
|
|
|
|
|
gravatar_url = f"https://www.gravatar.com/avatar/{hashlib.md5(email_encoded.lower()).hexdigest()}?{gravatar_params}"
|
|
|
|
|
|
|
|
|
|
cache.set(cache_key, gravatar_url, 60 * 60 * 10)
|
|
|
|
|
logger.info(f'set gravatar cache.key: {cache_key}')
|
|
|
|
|
return gravatar_url
|
|
|
|
|
"""生成Gravatar头像URL(优先使用OAuth用户头像,缓存10小时)"""
|
|
|
|
|
cache_key = f'gravatar/{email}'
|
|
|
|
|
cached_avatar_url = cache.get(cache_key)
|
|
|
|
|
|
|
|
|
|
if cached_avatar_url:
|
|
|
|
|
return cached_avatar_url
|
|
|
|
|
|
|
|
|
|
# 优先查询OAuth用户的自定义头像
|
|
|
|
|
oauth_user_list = OAuthUser.objects.filter(email=email)
|
|
|
|
|
if oauth_user_list:
|
|
|
|
|
valid_oauth_users = [user for user in oauth_user_list if user.picture is not None]
|
|
|
|
|
if valid_oauth_users:
|
|
|
|
|
cache.set(cache_key, valid_oauth_users[0].picture, 60 * 60 * 10)
|
|
|
|
|
return valid_oauth_users[0].picture
|
|
|
|
|
|
|
|
|
|
# 生成Gravatar默认头像链接
|
|
|
|
|
encoded_email = email.encode('utf-8')
|
|
|
|
|
default_avatar = static('blog/img/avatar.png')
|
|
|
|
|
gravatar_query_params = urllib.parse.urlencode({
|
|
|
|
|
'd': default_avatar,
|
|
|
|
|
's': str(size)
|
|
|
|
|
})
|
|
|
|
|
generated_gravatar_url = (
|
|
|
|
|
f"https://www.gravatar.com/avatar/"
|
|
|
|
|
f"{hashlib.md5(encoded_email.lower()).hexdigest()}"
|
|
|
|
|
f"?{gravatar_query_params}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
cache.set(cache_key, generated_gravatar_url, 60 * 60 * 10)
|
|
|
|
|
logger.info(f'Gravatar缓存设置成功(key: {cache_key})')
|
|
|
|
|
return generated_gravatar_url
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.filter
|
|
|
|
|
def gravatar(email, size=40):
|
|
|
|
|
url = gravatar_url(email, size)
|
|
|
|
|
return mark_safe(f'<img src="{url}" height="{size}" width="{size}">')
|
|
|
|
|
"""生成Gravatar头像HTML标签"""
|
|
|
|
|
avatar_url = gravatar_url(email, size)
|
|
|
|
|
return mark_safe(f'<img src="{avatar_url}" height="{size}" width="{size}" alt="用户头像">')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.simple_tag
|
|
|
|
|
def query(qs, **kwargs):
|
|
|
|
|
return qs.filter(**kwargs)
|
|
|
|
|
"""模板中过滤QuerySet(简化模板内的查询逻辑)"""
|
|
|
|
|
return qs.filter(** kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@register.filter
|
|
|
|
|
def addstr(arg1, arg2):
|
|
|
|
|
"""字符串拼接过滤器(支持模板中两个值的字符串连接)"""
|
|
|
|
|
return str(arg1) + str(arg2)
|