Django 博客 - 7 侧边栏

前言

一般博客主页都有侧边栏,分别有阅读排行、文章分类和文章存档等栏目,可以在视图函数将这些内容传递给模板,在模板里展示

如果在另一个页面也要显示这些栏目,就会造成很多重复代码。可以使用自定义模板标签来减少这些重复,同时也不用改变已有的视图函数

自定义模板标签

新建blog/templatetags目录,创建post_tags.py,文件名随意

post_tags.py里添加

from django import template
register = template.Library()

例如要添加一个显示最近五篇文章的栏目,只要用register.simple_tag装饰我们的函数

@register.simple_tag
def get_recent_post():
    return Post.objects.all()[:5]

由于Post模型默认按照创建时间降序,所以只需取前5篇就可以了

在模板文件里,需要使用load标签通过文件名加载自定义标签{% load post_tags %}
接着就能用{% get_recent_post as recent_post %}来获取最近文章列表了

{% load post_tags %}
{% get_recent_post as recent_post %}
<ul>
   {% for p in recent_post %}
   <li><a href="{{ p.get_absolute_url }}">{{ p.title }}</a></li>
   {% endfor %}
</ul>

有不明白的可以看自定义模板标签的官方文档

文章分类

编写标签函数

@register.simple_tag
def get_categories():
    return Category.objects.all()

在模板里引用

{% get_categories as categories %}
<ul>
    <li>文章分类</li>
    {% for category in categories %}
        <li><a href="{{ category.get_absolute_url }}">{{ category.name }} ({{ category.post_set.count }})</a>
        </li>
    {% endfor %}
</ul>

为了方便获取Categoryurl,在Category添加一个get_absolute_url方法

def get_absolute_url(self):
   return '%s#%s' % (reverse('blog:category'), self.name)

只是简单的在blog:categoryurl加上#名字,这样就可以通过http://127.0.0.1/category/#名字跳转到对应的分类

阅读排行

Post模型已经有views字段了,但是没引用。我们需要在每次浏览器浏览对应文章的时候在views变量加1
可以通过在详情视图的get方法里对views变量加1,并保持到数据库里

首先在Post模型新增increase_views方法

def increase_views(self):
    self.views += 1
    self.save(update_fields=['views'])

blog/views.pyPostView里覆写def get(self, request, *args, **kwargs)方法

这个get方法在父类BaseDetailView

class BaseDetailView(SingleObjectMixin, View):
    """
    A base view for displaying a single object
    """
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

BaseDetailViewget方法做了三个动作
通过get_object方法获取当前的模型对象,我们的应用里对应的就是一篇文章
通过get_context_data方法获取当前模板上下文
通过render_to_response渲染模板

所以在PostView里,覆写get方法,先调用父类的get方法就能获取当前的Post实例self.object,然后调用increase_views方法

def get(self, request, *args, **kwargs):
    response = super(PostView, self).get(request, *args, **kwargs)
    self.object.increase_views()
    return response

这样每次访问都会增加访问数了

接下来在blog/templatetags/post_tags.py添加一个新的模板标签get_most_read_post,这次可以传递一个参数num,可以在模板里传入,默认是5篇,只需要将Postviews的降序排序返回指定数量就完成了

@register.simple_tag
def get_most_read_post(num=5):
    return Post.objects.all().order_by('-views')[:num]

在模板里可以这么使用

{% get_most_read_post 10 as most_read_post %}
<ul>
    <li>阅读排行</li>
    {% for p in most_read_post %}
    <li><a href="{{ p.get_absolute_url }}">{{ p.title }} ({{ p.views }})</a></li>
    {% endfor %}
</ul>

标签

Category模型一样,Tag模型也添加一个get_absolute_url方法

def get_absolute_url(self):
    return '%s#%s' % (reverse('blog:tag'), self.name)

blog/templatetags/post_tags.py添加一个新的模板标签get_tags

from django.db.models import Count
@register.simple_tag
def get_tags():
    return Tag.objects.annotate(post_num=Count('post')).filter(post_num__gt=0)

这里过滤掉文章数量为0的标签,由于TagPost是多对多关系,可以通过len(tag.post_set.all())来获取每个TagPost的数量,但是这样太麻烦了,可以通过annotate方法,直接对Post计数,添加一个post_num属性,然后通过filter方法进行过滤

annotate的详细使用方法请查看官方文档

和上面的其他自定义标签一样,在模板中可以这样使用

{% get_tags as tags %}
<ul>
    <li>标签</li>
    <li><p>
        {% for tag in tags %}
        <a href="{{ tag.get_absolute_url }}">{{ tag.name }}</a>
        {% endfor %}
    </p></li>
</ul>

文章存档

blog/templatetags/post_tags.py添加一个新的模板标签get_posts

@register.simple_tag
def get_posts(num=None):
    return Post.objects.all()[:num]

在模板中显示时,也要和上一篇文章所讲的归档页面一样,需要通过regroup标签对文章依次按年和月进行重新分组,这里并不需要显示每一篇的标题,只是显示有发布文章的年份月份和对应的文章数量,通过month.list|length来获取每个具体年月的文章数量

<ul>
    <li>文章存档</li>
    {% get_posts as posts %}
    {% regroup posts by created_time.year as posts_by_year %}
    {% for year in posts_by_year %}
        {% regroup year.list by created_time.month as posts_by_month %}
        {% for month in posts_by_month %}
            <li><a href="{% url 'blog:archive' %}#
            {{ year.grouper }}{{ month.grouper|stringformat:'02d' }}">{{ year.grouper }}{{ month.grouper }}月
            ({{ month.list|length }})</a></li>
        {% endfor %}
    {% endfor %}
</ul>

这里的每一项的href中的url,都是用'blog:archive'对应的路径加上#年月组成,月份使用了一个模板过滤器stringformat:'02d',使得单个数字的月份会在前面补0,当然了,在archive页面也需要这么处理,才能正确跳转

本文相关源码

猜你喜欢

转载自blog.csdn.net/abc_1234d/article/details/78324835