【Django】検索機能を実装する|簡単な掲示板アプリの作成

DjangoでWebアプリに検索機能を実装する方法を解説します。HTMLフォームからGETリクエストでキーワードを受け取り、ビューでその値を使ってデータベースを検索します。モデルのfilterメソッドと、部分一致(__icontains)や前方一致(__startswith)といった条件を使い分けてデータを絞り込む、実践的な流れが学べます。

作成日: 更新日:

開発環境

  • OS: Windows10
  • Visual Studio Code: 1.73.0
  • Python: 3.10.11
  • Django: 5.0.3

サンプルコード

/app/urls.py

/app/urls.py
1     path('<int:pk>/comment/', login_required(views.comment_create), name='comment_create'),
2     path('<int:board_pk>/comment/<int:comment_pk>/delete/', login_required(views.comment_delete), name='comment_delete'),
3     path('my_boards/', login_required(views.my_boards), name='my_boards'),
4+    path('search/', views.board_search, name='search')
5 ]
6

/app/views.py

/app/views.py
1 
2     return redirect('show', pk=board_pk)
3 
4+def board_search(request):
5+    query = request.GET.get('query')
6+    search_type = request.GET.get('search_type')
7+    boards = Board.objects.all()
8+
9+    if search_type == 'prefix':
10+        # 前方一致
11+        boards = boards.filter(title__startswith=query)
12+    elif search_type == 'suffix':
13+        # 後方一致
14+        boards = boards.filter(title__endswith=query)
15+    elif search_type == 'partial':
16+        # 部分一致
17+        boards = boards.filter(title__icontains=query)
18+    else:
19+        # デフォルトは部分一致
20+        boards = boards.filter(title__icontains=query)
21+
22+    return render(request, 'index.html', {'boards': boards})
23+
24 
25 # ログインページのビュー
26 class CustomLoginView(LoginView):
27

/templates/index.html

/templates/index.html
1 {% block content %}
2     <section>
3         <h2>みんなの投稿一覧</h2>
4+        <form method="GET" action="{% url 'search' %}" class="d-flex flex-row mb-3">
5+            <div class="col-md-3">
6+                <input type="text" name="query" id="id_query" class="form-control" placeholder="検索">
7+            </div>
8+            <div class="col-md-2">
9+                <select name="search_type" class="form-select">
10+                    <option value="partial">部分一致</option>
11+                    <option value="prefix">前方一致</option>
12+                    <option value="suffix">後方一致</option>
13+                </select>
14+            </div>
15+            <div class="col-md-1">
16+                <button type="submit" class="btn btn-light">検索</button>
17+            </div>
18+        </form>
19         <table class="table">
20             <colgroup>
21                 <col style="width: 10%">
22

コード解説

変更点: 検索処理用のURLパターンを追加

/app/urls.py
1+    path('search/', views.board_search, name='search')
2

urls.pyファイルに、検索機能のための新しいURLパスを追加しました。

path('search/', views.board_search, name='search')という一行は、Webサイトの/search/というURLへのアクセスがあった場合に、views.pyファイルに定義されているboard_searchという関数を呼び出すように設定しています。

name='search'と名前を付けることで、HTMLテンプレート側から{% url 'search' %}のようにして、このURLを簡単に呼び出せるようになります。

変更点: 検索フォームをHTMLに追加

/templates/index.html
1+        <form method="GET" action="{% url 'search' %}" class="d-flex flex-row mb-3">
2+            <div class="col-md-3">
3+                <input type="text" name="query" id="id_query" class="form-control" placeholder="検索">
4+            </div>
5+            <div class="col-md-2">
6+                <select name="search_type" class="form-select">
7+                    <option value="partial">部分一致</option>
8+                    <option value="prefix">前方一致</option>
9+                    <option value="suffix">後方一致</option>
10+                </select>
11+            </div>
12+            <div class="col-md-1">
13+                <button type="submit" class="btn btn-light">検索</button>
14+            </div>
15+        </form>
16

投稿一覧を表示するindex.htmlに、検索フォームを追加しました。

<form>タグでフォーム全体を定義しています。method="GET"は、フォームの入力内容をURLの末尾に付けて送信する方法です。例えば、「django」と検索すると、URLは .../search/?query=django&search_type=partial のようになります。action="{% url 'search' %}"は、フォームの送信先を先ほどurls.pyで設定した/search/に指定しています。

<input type="text" name="query">は、ユーザーが検索キーワードを入力するテキストボックスです。name="query"という名前が重要で、この名前を使ってビュー側で入力値を取得します。

<select name="search_type">は、検索の種類(部分一致、前方一致、後方一致)を選ぶためのドロップダウンリストです。こちらもname="search_type"という名前で、選択された値がビューに送られます。

<button type="submit">は、このフォームの内容を送信するための検索ボタンです。

変更点: 検索処理を行うビュー関数を追加

/app/views.py
1+def board_search(request):
2+    query = request.GET.get('query')
3+    search_type = request.GET.get('search_type')
4+    boards = Board.objects.all()
5+
6+    if search_type == 'prefix':
7+        # 前方一致
8+        boards = boards.filter(title__startswith=query)
9+    elif search_type == 'suffix':
10+        # 後方一致
11+        boards = boards.filter(title__endswith=query)
12+    elif search_type == 'partial':
13+        # 部分一致
14+        boards = boards.filter(title__icontains=query)
15+    else:
16+        # デフォルトは部分一致
17+        boards = boards.filter(title__icontains=query)
18+
19+    return render(request, 'index.html', {'boards': boards})
20

views.pyに、検索処理を実行するためのboard_search関数を新しく定義しました。この関数が、検索フォームから送られてきたデータを受け取り、データベースを検索する中心的な役割を担います。

まず、request.GET.get('query')request.GET.get('search_type')で、HTMLフォームから送られてきた検索キーワードと検索条件の値を取得します。

次に、Board.objects.all()で、一旦データベースにある全ての投稿データを取得します。

その後のif文で、search_typeの値に応じて検索条件を切り替えています。

  • boards.filter(title__startswith=query): titleフィールドが検索キーワードで始まるデータ(前方一致)を絞り込みます。
  • boards.filter(title__endswith=query): titleフィールドが検索キーワードで終わるデータ(後方一致)を絞り込みます。
  • boards.filter(title__icontains=query): titleフィールドに検索キーワードが含まれるデータ(部分一致)を絞り込みます。iは、大文字と小文字を区別しないことを意味します。

filter()メソッドは、条件に合致するデータだけを絞り込むための命令です。title__startswithのように、フィールド名と検索条件を__(アンダースコア2つ)で繋ぐのがDjangoの書き方のルールです。

最後に、render()関数を使って、絞り込んだ結果のboardsindex.htmlテンプレートに渡し、検索結果一覧ページとして表示しています。これにより、同じテンプレートファイルを使いながら、表示する内容だけを検索結果に変えることができます。

おわりに

今回は、Djangoで検索機能を実装する具体的な流れを学習しました。HTMLの<form>からGETリクエストでキーワードを送り、views.pyで受け取ってfilter()メソッドでデータを絞り込む、というWeb開発の基本的な仕組みを体験しました。特に、title__icontainstitle__startswithのようにアンダースコア2つで条件を指定することで、部分一致や前方一致といった柔軟な検索が簡単に実装できる点がポイントです。絞り込んだ結果は既存の一覧テンプレートに渡して表示することで、効率的に検索結果画面を作成できることも理解できたはずです。

関連コンテンツ