不论是最新文章列表也好、最热文章列表也罢,都是把全部的文章数据所有展现给了用户。html
可是若是用户只关心某些特定类型的文章,抽取所有数据就显得既不方便、又不效率了。python
所以,给用户提供一个搜索功能,提供给用户感兴趣的几篇文章,就大有用处了。git
尽管细节不一样,可是搜索和列表有不少相似的地方:它们都是先检索出一些文章对象,并将其展现给用户。上一章已经说过,代码重复是万恶之源,好的实践必须把功能相似的模块尽可能复用起来。基于这个原则,咱们打算继续在原有的article_list()
上添砖加瓦,让其功能更加的强大。github
随着项目愈来愈庞大,又须要将功能复杂的模块拆分红更简单的多个模块。目前咱们还不用担忧这个问题。
更酷的是,咱们但愿搜索出来的文章也可以按照时间、热度等各类方式进行排序。所以须要构造一个新的参数search
,可以和以前的order
参数进行联合查询。django
用户搜索内容时提交的文本,能够用GET请求提交,也能够用POST请求提交。根据实际的须要进行选择。测试
由于order
是用GET提交的,而且翻页是GET请求,所以选择GET方式提交搜索文本,能够方便地和以前的模块结合起来。优化
以前咱们已经用过表单组件<form method="POST">
,经过POST请求提交数据。表单组件一样也能够提交GET请求,只要去掉method="POST"
属性就能够了。网站
Model.objects.all()
可以返回表中的全部对象。ui
对应的,Model.objects.filter(**kwargs)
能够返回与给定参数匹配的部分对象。url
还有
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) ...
重点知识以下:
留意filter中Q对象的用法。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"> ...
search
参数search
参数;required
属性阻止用户提交空白文本Emmm...想一想也不用改动其余东西了。
开始测试吧!
仍是打开文章列表页面:
出现了搜索栏!而且翻页、最热等功能一切正常。
在搜索栏中输入“PYTHON”,结果以下:
成功将标题或正文中含有"python"关键字的文章检索出来了,而且是忽略大小写的。点击最热可让检索结果按浏览量排序,翻页功能也正常工做。很好,达成了目标!
学到这里的读者应该感到自豪:你用了同一个url,集成了不少种功能,展现了不一样的内容!这对新手来讲其实并不容易作到。
这种方法有一个小缺点:有的时候url中会包含像
search=''
(空值)这样无心义的字符串,强迫症简直不能忍。所幸这无伤大雅,一般用户并不会关心你的url是什么样子的,只要网页美观好用就行。
本章完成了一个简单的搜索功能,这对于我的博客来讲应该够用了。
更加复杂、深度定制的搜索能够借助第三方模块,如Haystack。
另外笔者这样实现搜索不必定是最优的。相信你已经掌握多种途径来实现搜索功能了(POST请求?搜索专用视图?另写url?),尽情尝试一番吧。
转载请注明出处。