Django搭建个人博客:根据浏览量对最热文章排序

1682阅读 · 14评论 · 2018/12/30发布   前往评论

有了浏览量之后,文章受欢迎的程度就有了评价标准。随之而来的就有根据浏览量对文章进行排序的需求,即显示“最热文章”

现在你已经很熟悉MTV模式,不需要我啰嗦也能完成任务:

  • 文章的模型已经有了,不需要写Model了
  • 写一个视图函数article_list_by_views(),取出按浏览排序后的文章对象
  • 将文章对象传递到模板,并进行渲染

很简单,但也隐藏着问题:最热文章列表和之前的普通文章列表相比,大部分功能其实都是相同的,仅仅是排序不同而已。

万一哪天需要根据文章标题排序呢?万一还需要用户id排序、标签排序、收藏排序...不仅如此,就连路由urls.py都要跟着膨胀。代码会越来越臃肿且不可维护。

重复的代码是万恶之源。因此这里挑战一下,不创建新的视图/路由,而是将排序功能融合到已有的视图/路由中。

视图

根据以上需求,重写article_list()

article/views.py

...
# 重写文章列表
def article_list(request):
    # 根据GET请求中查询条件
    # 返回不同排序的对象数组
    if request.GET.get('order') == 'total_views':
        article_list = ArticlePost.objects.all().order_by('-total_views')
        order = 'total_views'
    else:
        article_list = ArticlePost.objects.all()
        order = 'normal'

    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)

    # 修改此行
    context = { 'articles': articles, 'order': order }

    return render(request, 'article/list.html', context)

重点知识如下:

  • 前面用过GET请求传递单个参数。它也是可以传递多个参数的,如?a=1&b=2,参数间用&隔开
  • 视图根据GET参数order的值,判断取出的文章如何排序
  • order_by()方法指定对象如何进行排序。模型中有total_views这个整数字段,因此‘total_views’为正序,‘-total_views’为逆序
  • 为什么把新变量order也传递到模板中?因为文章需要翻页!order给模板一个标识,提醒模板下一页应该如何排序

这样一来,排序所需要的参数都可以通过查询获得,连urls.py都不用改写了。

模板

接下来修改文章列表模板:优化入口,并且正确分页:

templates/article/list.html

...

<div class="container">
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views">
                    最热
                </a>
            </li>
        </ol>
    </nav>

    <div class="row mt-2">
        {% for article in articles %}
        ...
        {% endfor %}
    </div>

<!-- 页码导航 -->
...
<a href="?page=1&order={{ order }}" class="btn btn-success">&laquo; 1</a>
...
<a href="?page={{ articles.previous_page_number }}&order={{ order }}" 
   class="btn btn-secondary">...</a>
...
    {% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}&order={{ order }}"
   class="btn btn-secondary">{{ articles.next_page_number }}</a>
...
<a href="?page={{ articles.paginator.num_pages }}&order={{ order }}"
   class="btn btn-success">{{ articles.paginator.num_pages }} &raquo;</a>
...
  • 新增了Bootstrap中的面包屑导航样式breadcrumb
  • 页码导航中,所有的分页链接都新增了order参数

测试

启动服务器,点击“最热”:

工作得很好!切换页码,留意地址栏中是如何变化的。

还剩一个小瑕疵:用户点击“最热”按钮后,此按钮最好能够变为灰色,并且不可点击。这个精益求精的机会就留给读者去优化吧。

header.html中有一个小改动:"写文章"的入口被挪到用户下拉菜单中了。

总结

本章已经摸到一个高级的编程领域门槛了:代码复用。将类似功能的代码合并到了一起,并且让后续的功能扩展变得很容易。只需要在视图中写几个elif语句就搞定了。

在读者以后的编程中,也要尽量优化代码结构,达到事半功倍的效果。

至此,博客虽小,功能却相当完整了。继续努力!





<< 上一章 下一章 >>
本文作者: 杜赛
发布时间: 2018年12月30日 - 21:19
最后更新: 2019年01月27日 - 21:50
知识共享许可协议   转载请保留原文链接及作者



登录 后回复

共有14条评论

avatar
只能这样啦 么么哒! 1

赞,网站这个比Github 那个改善很大。

7个月前 回复


avatar
杜赛 [博主] 只能这样啦 么么哒! 1

谢谢。 Github是指我的教程里写的那个项目吗?

入门教程肯定越简单越好,我的博客功能太琐碎了。

7个月前 回复


avatar
只能这样啦 杜赛 [博主] 么么哒! 1

是的呢,向你学习。刚开始接触写网页,谢谢版主给的参考

7个月前 回复


avatar
杜赛 [博主] 只能这样啦 么么哒! 1

不客气。 教程还在持续更新中,以后会逐渐把有用的高级功能都展开讲解,包括我的网站中用到的。

业余时间写教程,非常耗费精力,速度肯定是快不起来了。

简单问题在我的博客留言就好,复杂的Email咨询。

7个月前 回复


avatar
waynecanfly 么么哒! 1
本回复已被 waynecanfly 删除

3个月前 回复


avatar
pidada 么么哒! 1

原来“写文章”的入口是在这里变的  laugh

3个月前 回复


avatar
ac1864 么么哒! 1

过关,

但是,模板还是用的copy paste。

2个月前 回复


avatar
pengshilin 么么哒! 1

if request.GET.get('order') == 'total_views':

请问这里的 'order'  是什么呢? 

2个月前 回复


avatar
杜赛 [博主] pengshilin 么么哒! 1

order是在视图里定义的一个变量,用来判断文章列表应该如何排序

2个月前 回复


avatar
pengshilin pengshilin 么么哒! 1

<a href="{% url 'article:article_list' %}?order=total_views">

是视图这里传进去的吗

2个月前 回复


avatar
杜赛 [博主] pengshilin 么么哒! 1

对,模板和视图互相传递这个值

2个月前 回复


avatar
test1 么么哒! 1

支持!,感谢!

23天前 回复


avatar
jack 么么哒! 1

大神,order=normal是什么意思?默认排序的吗?

8天前 回复


avatar
杜赛 [博主] jack 么么哒! 1

7天前 回复