Django搭建个人博客:简单搜索博客文章

喜儿
• 阅读 4159

不管是最新文章列表也好、最热文章列表也罢,都是把所有的文章数据全部展示给了用户。

但是如果用户只关心某些特定类型的文章,抽取全部数据就显得既不方便、又不效率了。

因此,给用户提供一个搜索功能,提供给用户感兴趣的几篇文章,就大有用处了。

准备工作

逻辑

尽管细节不同,但是搜索和列表有很多类似的地方:它们都是先检索出一些文章对象,并将其展示给用户。上一章已经说过,代码重复是万恶之源,好的实践必须把功能类似的模块尽量复用起来。基于这个原则,我们打算继续在原有的article_list()上添砖加瓦,让其功能更加的强大。

随着项目越来越庞大,又需要将功能复杂的模块拆分成更简单的多个模块。目前我们还不用担心这个问题。

更酷的是,我们希望搜索出来的文章也能够按照时间、热度等各种方式进行排序。因此需要构造一个新的参数search,能够和之前的order参数进行联合查询。

GET还是POST?

用户搜索内容时提交的文本,可以用GET请求提交,也可以用POST请求提交。根据实际的需要进行选择。

因为order是用GET提交的,并且翻页是GET请求,因此选择GET方式提交搜索文本,可以方便地和之前的模块结合起来。

之前我们已经用过表单组件<form method="POST">,通过POST请求提交数据。表单组件同样也可以提交GET请求,只要去掉method="POST"属性就可以了。

Q对象

Model.objects.all()能够返回表中的所有对象。

对应的,Model.objects.filter(**kwargs)可以返回与给定参数匹配的部分对象。

还有Model.objects.exclude(**kwargs)返回与给定参数不匹配的对象

如果想对多个参数进行查询怎么办?比如同时查询文章标题和正文内容。这时候就需要Q对象

视图

那么按照前面说好的,修改article_list()

article/views.py

...

# 引入 Q 对象
from django.db.models import Q

def article_list(request):
    search = request.GET.get('search')
    order = request.GET.get('order')
    # 用户搜索逻辑
    if search:
        if order == 'total_views':
            # 用 Q对象 进行联合搜索
            article_list = ArticlePost.objects.filter(
                Q(title__icontains=search) |
                Q(body__icontains=search)
            ).order_by('-total_views')
        else:
            article_list = ArticlePost.objects.filter(
                Q(title__icontains=search) |
                Q(body__icontains=search)
            )
    else:
        # 将 search 参数重置为空
        search = ''
        if order == 'total_views':
            article_list = ArticlePost.objects.all().order_by('-total_views')
        else:
            article_list = ArticlePost.objects.all()

    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)
    
    # 增加 search 到 context
    context = { 'articles': articles, 'order': order, 'search': search }
    
    return render(request, 'article/list.html', context)

...

重点知识如下:

  • 新增参数search,存放需要搜索的文本。若search不为空,则检索特定文章对象。
  • 留意filterQ对象的用法。Q(title__icontains=search)意思是在模型的title字段查询,icontains不区分大小写的包含,中间用两个下划线隔开。search是需要查询的文本。多个Q对象用管道符|隔开,就达到了联合查询的目的。

    icontains不区分大小写,对应的contains区分大小写
  • 为什么需要search = ''语句?如果用户没有搜索操作,则search = request.GET.get('search')会使得search = None,而这个值传递到模板中会错误地转换成"None"字符串!等同于用户在搜索“None”关键字,这明显是错误的。

    完成本章内容后,可以删除此语句看看效果

除此之外还有一点小的代码优化工作:将需要重复用到order = request.GET.get('order')提取到顶部,让模块稍稍清爽一点。

模板

还是修改文章列表的模板文件。

需要修改的内容稍多,仔细一些不要看错:

templates/article/list.html

...

<div class="container">
    <!-- 修改,面包屑的href增加search参数 -->
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?search={{ search }}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views&search={{ search }}">
                    最热
                </a>
            </li>
        </ol>
    </nav>

    <!-- 新增,搜索栏 -->
    <div class="row">
        <div class="col-auto mr-auto">
            <form class="form-inline" >
                <label class="sr-only">content</label>
                <input type="text" 
                    class="form-control mb-2 mr-sm-2" 
                    name="search" 
                    placeholder="搜索文章..." 
                    required
                >
            </form>
        </div>
    </div>

    <!-- 新增,搜索提示语 -->
    {% if search %}
        {% if articles %}
            <h4><span style="color: red">"{{ search }}"</span>的搜索结果如下:</h4>
            <hr>        
        {% else %}
            <h4>暂无<span style="color: red">"{{ search }}"</span>有关的文章。</h4>
            <hr>
        {% endif %}
    {% endif %}
        
            
...
    
<!-- 修改,页码href增加search参数 -->
<a href="?page=1&order={{ order }}&search={{ search }}" class="btn btn-success">
...
<a href="?page={{ articles.previous_page_number }}&order={{ order }}&search={{ search }}" class="btn btn-secondary">
...
<a href="?page={{ articles.next_page_number }}&order={{ order }}&search={{ search }}" class="btn btn-secondary">
...
<a href="?page={{ articles.paginator.num_pages }}&order={{ order }}&search={{ search }}"class="btn btn-success">

...
  • 面包屑组件、页码组件都改动了href:增加了search参数
  • 新增搜索栏,以GET请求提交search参数;required属性阻止用户提交空白文本
  • 新增搜索提示语。好的UI必须让用户了解当前的状态

Emmm...想想也不用改动其他东西了。

开始测试吧!

测试

还是打开文章列表页面:

Django搭建个人博客:简单搜索博客文章

出现了搜索栏!并且翻页、最热等功能一切正常。

在搜索栏中输入“PYTHON”,结果如下:

Django搭建个人博客:简单搜索博客文章

成功将标题或正文中含有"python"关键字的文章检索出来了,并且是忽略大小写的。点击最热可以让检索结果按浏览量排序,翻页功能也正常工作。很好,达成了目标!

学到这里的读者应该感到自豪:你用了同一个url,集成了很多种功能,展示了不同的内容!这对新手来说其实并不容易做到。

这种方法有一个小缺点:有的时候url中会包含像search=''(空值)这样无意义的字符串,强迫症简直不能忍。所幸这无伤大雅,通常用户并不会关心你的url是什么样子的,只要网页美观好用就行。

总结

本章完成了一个简单的搜索功能,这对于个人博客来说应该够用了。

更加复杂、深度定制的搜索可以借助第三方模块,如Haystack

另外笔者这样实现搜索不一定是最优的。相信你已经掌握多种途径来实现搜索功能了(POST请求?搜索专用视图?另写url?),尽情尝试一番吧。

转载请注明出处。
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Easter79 Easter79
4年前
Springboot集成flowable modeler web流程设计器
之前画流程图都是用tomcat启动flowablemodeler,但是这样启动就不能在分配任务用户/用户组的时候查询自己系统里的数据。所以现在需要把flowablemodeler集成到项目里来。之前自己也搜索了很多文章,都感觉不是很清晰,可能也是因为我刚接触不久。现在自己集成好了之后,记录一下自己学习的结果。1.首先需要创建一个springb
Stella981 Stella981
4年前
Django的ListView超详细用法(含分页paginate功能)
开发环境:python3.6django1.11场景一经常有从数据库中获取一批数据,然后在前端以列表的形式展现,比如:获取到所有的用户,然后在用户列表页面展示。解决方案常规写法是,我们通过Django的ORM查询到所有的数据,然后展示出来,代码如下:defuser_list(request
Easter79 Easter79
4年前
Swift项目兼容Objective
!456.jpg(http://static.oschina.net/uploads/img/201509/13172704_1KcG.jpg"1433497731426906.jpg")本文是投稿文章,作者:一叶(博客(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2F00red
Stella981 Stella981
4年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
4年前
Linux下使用 github+hexo 搭建个人博客07
这是搭建个人博客系统系列文章的最后一篇,如果你是从第一篇一路跟下来的,那么恭喜你,即将完成整个博客网站的搭建。OK,话不多说,开始我们的收官之战。不知你想过没有,如果我们的文章少,一眼看完整个目录,那么还好。但是如果日积月累几年下来,我们的文章增加到100以上,那么不管是你博主,还是访问用户,如何去快速找到所需的文章呢。这时我们就需要用到搜索了。
Stella981 Stella981
4年前
Lucene 8.x 使用 FunctionScoreQuery 实现自定义的文档评分
在使用不管是Lucene或者ElasticSearch进行全文搜索中,检索到文档很简单,但是把搜索者最想要的结果排在最前面,这是最具挑战性的。我们举个最简单的例子:假设我们需要做一个博客搜索,当我们用Lucene默认的匹配度进行排序,那你可能会发现搜索出来的都是一堆垃圾文章。所以一般我们会把博客文章的其他因素作为排序方法来替代匹配度。例如会考
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这