template标签_Django实战: 利用自定义模板标签实现仿CSDN博客月度归档
應(yīng)網(wǎng)友慕之巖的請(qǐng)求,現(xiàn)提供下Django項(xiàng)目中如何使用自定義標(biāo)簽實(shí)現(xiàn)仿CSDN博客的月度歸檔(如下圖所示)。要求按月統(tǒng)計(jì)每個(gè)月發(fā)表的博文篇數(shù), 跳過空白月份,最后結(jié)果按發(fā)布時(shí)間逆序排列。點(diǎn)擊每個(gè)月份可以看到詳細(xì)博文列表清單。本文著重講述如何實(shí)現(xiàn),不包括樣式美化。如代碼手機(jī)上看不全,可以搜索知乎大江狗或csdn大江狗。
實(shí)現(xiàn)原理
假如我們有如下一個(gè)Article模型(完整的見Django實(shí)戰(zhàn)專題: 開發(fā)專業(yè)博客(1)之內(nèi)容管理后臺(tái)開發(fā)),里面包含了status和發(fā)布日期pub_date兩個(gè)關(guān)鍵字段。
class Article(models.Model):"""文章模型"""STATUS_CHOICES = ( ('d', '草稿'),('p', '發(fā)表'),) title = models.CharField('標(biāo)題', max_length=200, db_index=True) slug = models.SlugField('slug', max_length=60, blank=True) body = RichTextUploadingField('正文') pub_date = models.DateTimeField('發(fā)布時(shí)間', null=True) create_date = models.DateTimeField('創(chuàng)建時(shí)間', auto_now_add=True) mod_date = models.DateTimeField('修改時(shí)間', auto_now=True) status = models.CharField('文章狀態(tài)', max_length=1, choices=STATUS_CHOICES, default='p')我們實(shí)現(xiàn)原理如下:
我們需要構(gòu)建一個(gè)由年份、月份和和當(dāng)月發(fā)表文章數(shù)量組成的字典列表。新的列表由如{'year': 2019, 'month': 8, total:'4'}這樣的多個(gè)字典組成。
我們先獲取所有已發(fā)表文章的最大年份和最小年份,縮小歸檔時(shí)間范圍。
利用for循環(huán)查詢最大年份和最小年份間每年的12個(gè)月中是否有文章發(fā)表(逆序查找),如果有,我們將統(tǒng)計(jì)該月發(fā)表文章的數(shù)量,并將由年份、月份和和文章數(shù)量構(gòu)建成的字典插入列表。如果當(dāng)月無文章發(fā)表,我們直接跳過。
最后我們?cè)谀0逯醒h(huán)輸出每條記錄的year, month和total即可。同時(shí)我們還需要為每條記錄建個(gè)url,用于展示對(duì)應(yīng)year和month的月度文章詳細(xì)列表。
自定義模板標(biāo)簽
我們將自定義一個(gè){% show_monthly_archive %}的標(biāo)簽,用于顯示月度歸檔。這樣做的好處是顯而易見的,因?yàn)槿绻銓硐朐诓煌撁姹热缥恼略斍椤⑽恼铝斜砘蛩阉黜撁骘@示月度歸檔信息時(shí),你只需要在相應(yīng)頁面模板加入這么一行代碼就可以了,而不需要更改視圖增加新的context,也不需要對(duì)模板進(jìn)行大的調(diào)整。
更多關(guān)于自定義模板標(biāo)簽的內(nèi)容見Django基礎(chǔ)(16): 模板標(biāo)簽(tags)的分類及如何自定義模板標(biāo)簽。本文只做簡(jiǎn)要介紹。首先你要在你的app目錄下新建一個(gè)叫templatetags的文件夾(不能取其它名字), 里面必需包含__init__.py的空文件。在該目錄下你還要新建一個(gè)python文件專門存放你自定義的模板標(biāo)簽函數(shù),本例中為blog_extras.py,當(dāng)然你也可以取其它名字。整個(gè)目錄結(jié)構(gòu)如下所示:
blog/? __init__.py
? models.py
? templatetags/
? ? ? __init__.py
? ? ? blog_extras.py
? views.py
在模板中使用自定義的模板標(biāo)簽時(shí),需要先使用{% load blog_extras %}載入自定義的過濾器,然后通過{% show_monthly_archive %}?使用它。
在blog_extras.py中添加如下代碼。該自定義標(biāo)簽的作用創(chuàng)建字典列表, ?并通過包含的模板_monthly_archive_list.html循環(huán)輸出列表結(jié)果。因?yàn)檫@個(gè)模板并不是用于顯示整個(gè)頁面的主模板,而只是些模板片段,所以我們?cè)谒懊婕恿讼聞澗€_。
#blog_extras.py
from django import templatefrom ..models import Articleregister = template.Library()# show rendered template@register.inclusion_tag('blog/_monthly_archive_list.html')def show_monthly_archive():#按日期逆序排序articles = Article.objects.filter(status='p').order_by('-pub_date')#獲取最大和最小年份max_year = articles[0].pub_date.year min_year = articles[len(articles)-1].pub_date.year#按年和月循環(huán),排除空月份,生成子一個(gè)字典列表rows = []for year in range(max_year, min_year -1, -1):for month in range(12, 0, -1): total = Article.objects.filter(pub_date__year=year,pub_date__month=month).count()if total > 0: rows.append({"year": year, "month": month, "total": total, })else:continue return {'rows': rows, }模板blog/_monthly_archive_list.html代碼如下:
歸檔{% for row in rows %}href="{% url 'blog:month_archive' row.year row.month %}">{{ row.year }}年{{ row.month }}月 {{ row.total }}篇{% endfor %}視圖views.py和urls.py
事情還沒結(jié)束。我們還需要自定義一個(gè)名為month_archive的視圖及其對(duì)應(yīng)urls來根據(jù)年份year和月份month來顯示文章清單。注意這里的month_archive.html是主模板哦,我們還加入了分頁。
#urls.py
path('articles///', views.month_archive, name='month_archive'),#views.py
def month_archive(request, year, month): articles = Article.objects.filter(status='p', pub_date__year=year,pub_date__month=month).order_by('-pub_date') paginator = Paginator(articles, 3) page = request.GET.get('page') page_obj = paginator.get_page(page) context = {'page_obj': page_obj, 'paginator': paginator, 'is_paginated': True}return render(request, 'blog/month_archive.html', context)#blog/month_archive.html
{% extends "blog/base.html" %}{% block content %}月度歸檔{# 注釋: page_obj不要改。Article可以改成自己對(duì)象 #}{% if page_obj %}class="table table-striped">標(biāo)題發(fā)布日期查看{% for article in page_obj %}{{ article.title }}{{ article.pub_date | date:"Y-m-d" }} href="{% url 'blog:article_detail' article.id article.slug %}">class="glyphicon glyphicon-eye-open">
{% endfor %}{% else %}{# 注釋: 這里可以換成自己的對(duì)象 #}沒有文章。{% endif %}{# 注釋: 下面代碼一點(diǎn)也不要?jiǎng)? #}{% if is_paginated %}class="pagination">{% if page_obj.has_previous %}class="page-item">class="page-link" href="?page={{ page_obj.previous_page_number }}">?{% else %}class="page-item disabled">class="page-link">?{% endif %} {% for i in paginator.page_range %} {% if page_obj.number == i %}class="page-item active">class="page-link"> {{ i }} class="sr-only">(current){% else %}class="page-item">class="page-link" href="?page={{ i }}">{{ i }}{% endif %} {% endfor %} {% if page_obj.has_next %}class="page-item">class="page-link" href="?page={{ page_obj.next_page_number }}">?{% else %}class="page-item disabled">class="page-link">?{% endif %}{% endif %}{% endblock %}
查看效果
我們們?cè)赼rticle_detail.html加入{% load blog_extras %}和?{% show_monthly_archive %}, 這時(shí)的文章詳情頁面就包含月度歸檔信息了。點(diǎn)擊鏈接可以查看每月文章列表。
最后的話
統(tǒng)計(jì)哪些月份是否表了文章以及每個(gè)月發(fā)表了多少篇文章是非常耗時(shí)的運(yùn)算。如果用戶每訪問一個(gè)頁面,都需要重新計(jì)算一次會(huì)造成很大的資源浪費(fèi)。這部分信息其實(shí)更新的并不是那么快,完全可以使用緩存把計(jì)算結(jié)果緩存起來,如下所示。更多內(nèi)容見Django基礎(chǔ)(8): 緩存Cache應(yīng)用場(chǎng)景及工作原理,Cache設(shè)置及如何使用。
#在模板中使用cache
{% load cache %}{% cache 500 %}
{% show_monthly_archive %}
{% endcache %}你有更好的實(shí)現(xiàn)辦法嗎? 如果你有什么功能或業(yè)務(wù)需求需要實(shí)現(xiàn)的,歡迎留言。小編我會(huì)根據(jù)個(gè)人興趣有選擇性地提供解決方案。
大江狗
2019.09.21
總結(jié)
以上是生活随笔為你收集整理的template标签_Django实战: 利用自定义模板标签实现仿CSDN博客月度归档的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TP-Link路由器设置上网知识笔记
- 下一篇: 电商数据分析Excel案例