1. 程式人生 > >Django 部落格實現簡單的全文搜尋

Django 部落格實現簡單的全文搜尋

作者:HelloGitHub-追夢人物

文中所涉及的示例程式碼,已同步更新到 HelloGitHub-Team 倉庫

搜尋是一個複雜的功能,但對於一些簡單的搜尋任務,我們可以使用 Django Model 層提供的一些內建方法來完成。現在我們來為我們的部落格提供一個簡單的搜尋功能。

概述

部落格文章通常包含標題和正文兩個部分。當用戶輸入某個關鍵詞進行搜尋後,我們希望為使用者顯示標題和正文中含有被搜尋關鍵詞的全部文章。整個搜尋的過程如下:

  1. 使用者在搜素框中輸入搜尋關鍵詞,假設為 “django”,然後使用者點選了搜尋按鈕提交其輸入的結果到伺服器。
  2. 伺服器接收到使用者輸入的搜尋關鍵詞 “django” 後去資料庫查詢文章標題和正文中含有該關鍵詞的全部文章。
  3. 伺服器將查詢結果返回給使用者。

整個過程就是這樣,下面來看看 Django 如何用實現這些過程。

將關鍵詞提交給伺服器

先來回顧一下我們的 Django 部落格的 Post(文章)模型:

blog/models.py

class Post(models.Model):
    # 標題
    title = models.CharField("標題", max_length=70)
    # 正文
    body = models.TextField("正文")
    
    # 其他屬性...
    
    def __str__(self):
        return self.title

先看到第 1 步,使用者在搜尋框輸入搜尋關鍵詞,因此我們要在部落格上為使用者提供一個搜尋表單,HTML 表單程式碼大概像這樣:

templates/base.html

<form role="search" method="get" id="searchform" action="{% url 'blog:search' %}">
  <input type="search" name="q" placeholder="搜尋" required>
  <button type="submit"><span class="ion-ios-search-strong"></span></button>
</form>

特別注意這裡 <input type="search" name="q" placeholder="搜尋" required> 中的 name 屬性,當用戶在這個 input 中輸入搜尋內容並提交表單後,鍵入的資料會以鍵值對的形式提交伺服器,這個鍵的名字就是通過 name 屬性指定的。這樣伺服器就可以根據 name 的值來取得使用者輸入的內容。

使用者輸入了搜尋關鍵詞並點選了搜尋按鈕後,資料就被髮送給了 Django 後臺伺服器。表單的 action 屬性的值為 {% url 'blog:search' %}(雖然我們還沒有寫這個檢視函式),表明使用者提交的結果將被髮送給 blog 應用下 search 檢視函式對應的 URL。

查詢含有搜尋關鍵詞的文章

搜尋的功能將由 search 檢視函式提供,程式碼寫在 blog/views.py 裡:

blog/views.py

from django.contrib import messages

def search(request):
    q = request.GET.get('q')

    if not q:
        error_msg = "請輸入搜尋關鍵詞"
        messages.add_message(request, messages.ERROR, error_msg, extra_tags='danger')
        return redirect('blog:index')

    post_list = Post.objects.filter(Q(title__icontains=q) | Q(body__icontains=q))
    return render(request, 'blog/index.html', {'post_list': post_list})

首先我們使用 request.GET.get('q') 獲取到使用者提交的搜尋關鍵詞。使用者通過表單 get 方法提交的資料 Django 為我們儲存在 request.GET 裡,這是一個類似於 Python 字典的物件,所以我們使用 get 方法從字典裡取出鍵 q 對應的值,即使用者的搜尋關鍵詞。這裡字典的鍵之所以叫 q 是因為我們的表單中搜索框 input 的 name 屬性的值是 q,如果修改了 name 屬性的值,那麼這個鍵的名稱也要相應修改。

接下來我們做了一個小小的校驗,如果使用者沒有輸入搜尋關鍵詞而提交了表單,我們就無需執行查詢,我們給給使用者發一條錯誤提醒訊息,這裡使用了 django messages 應用,這在 交流的橋樑:評論功能 中講過。然後將使用者重定向到首頁。這裡的 redirect 函式也在那篇教程中講過。

如果使用者輸入了搜尋關鍵詞,我們就通過 filter 方法從資料庫裡過濾出符合條件的所有文章。這裡的過濾條件是 title__icontains=q,即 title 中包含(contains)關鍵字 q,字首 i 表示不區分大小寫。這裡 icontains 是查詢表示式(Field lookups),我們在之前也使用過其他類似的查詢表示式,其用法是在模型需要篩選的屬性後面跟上兩個下劃線。Django 內建了很多查詢表示式,建議過一遍 Django 官方留個印象,瞭解每個表示式的作用,以後碰到相關的需求就可以快速定位到文件查詢其用途 Field lookups。

此外我們這裡從 from django.db.models 中引入了一個新的東西:Q 物件。Q 物件用於包裝查詢表示式,其作用是為了提供複雜的查詢邏輯。例如這裡 Q(title__icontains=q) | Q(body__icontains=q) 表示標題(title)含有關鍵詞 q 或者正文(body)含有關鍵詞 q ,或邏輯使用 | 符號。如果不用 Q 物件,就只能寫成 title__icontains=q, body__icontains=q,這就變成標題(title)含有關鍵詞 q 且正文(body)含有關鍵詞 q,就達不到我們想要的目的。

繫結 URL

有了檢視函式後記得把檢視函式對映到相應了 URL,如下。

blog/urls.py

urlpatterns = [
    # 其他 url 配置
    path('search/', views.search, name='search'),
]

大功告成,在導航欄嘗試輸入一些關鍵詞,看看效果吧!

當然這樣的搜尋功能是非常簡略的,難以滿足一些複雜的搜尋需求。編寫一個搜尋引擎是一個大工程,好在 django-haystack 這款第三方 app 為我們完成了全部工作。使用它我們可以實現更加複雜的搜尋功能,比如全文檢索、按搜尋相關度排序、關鍵字高亮等等類似於百度搜索的功能,功能十分強大。當然其使用也會複雜一些,下一篇教程將向大家介紹 django-haystack 結合 Elasticsearch 搜尋引擎的使用方法。


『講解開源專案系列』——讓對開源專案感興趣的人不再畏懼、讓開源專案的發起者不再孤單。跟著我們的文章,你會發現程式設計的樂趣、使用和發現參與開源專案如此簡單。歡迎留言聯絡我們、加入我們,讓更多人愛上開源、貢獻開源