不論是最新文章列表也好、最熱文章列表也罷,都是把全部的文章數據所有展現給了用戶。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?),盡情嘗試一番吧。
轉載請註明出處。