はじめに
学習のアウトプットとしてのメモ
簡単なブログアプリでサイト内検索機能を実装していく
環境
macOS
Django version 2.2.16
前提
Postモデルを持つ簡単なブログアプリ
一覧画面を汎用クラスビューListviewを利用してデータを取得、一覧表示している
検索機能はPostモデルのtitleとtextに対して検索をかける仕様
検索処理の流れ
1.URLに [/?query=任意の検索ワード] を生成させるフォームを作成
2.url.pyを経由してview.pyのListviewが呼び出される
3.Listview内のget_queryset関数でQオブジェクトを利用し、渡されてきたqueryが含まれたquerysetを返す
4.querysetがfor文で1つずつ取り出され一覧表示される
5.メッセージ機能で検索結果を表示する
手順
テンプレート側で検索フォームを作成する
~
<form method="get">
<input type="search" value="{{ request.GET.query }}"
name="query" type="text"
placeholder=" Input keywords..">
<button>Search</button>
</form>
~
Listview内の処理を追加する。get_queryset(self)関数から下の部分
~
from django.views.generic import ListView
from django.db.models import Q # 検索機能のために追加
~
class PostList(ListView):
context_object_name = 'post_list'
queryset = Post.objects.order_by('-created_date')
model = Post
paginate_by = 7
def get_queryset(self): # 検索機能のために追加
queryset = Post.objects.order_by('-created_date')
query = self.request.GET.get('query')
if query:
queryset = queryset.filter(
Q(title__icontains=query) | Q(text__icontains=query)
)
return queryset
~
表示部分
{% for post in post_list %}
<div class="content">
<div class="title">
<a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a>
</div>
<div class="datatime">{{ post.created_date|date:"Y-n-j" }}</div>
</div>
{% endfor %}
検索ワードをふくむpost_listをfor文で表示していく。
これで機能は実装できた
検索結果のメッセージ
- "検索ワード" を含む検索結果:
のようにメッセージ機能があると親切。viewで検索ワードを取得する。
from django.db.models import Q
from django.contrib import messages # 追加
~
~
if query:
queryset = queryset.filter(
Q(title__icontains=query) | Q(text__icontains=query)
)
messages.add_message(self.request, messages.INFO, query) # 追加
return queryset
~
以上の追加したコードでは、検索ワードをメッセージフレームワークを使ってテンプレートへ渡していく。任意でタグは指定する。
~
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class="{{ message.tags }}"{% endif %}>
"{{ message }}" を含む検索結果:
</li>
{% endfor %}
</ul>
{% endif %}
~
最後にテンプレート側でfor文で取り出された内容を表示していく。
paginate_by を使用している場合の検索クエリの引き継方法
class PostList(ListView):
context_object_name = 'post_list'
queryset = Post.objects.order_by('-created_date')
model = Post
paginate_by = 3 # コンテンツの表示上限の追加
def get_queryset(self):
queryset = Post.objects.order_by('-created_date')
query = self.request.GET.get('query')
if query:
queryset = queryset.filter(
Q(title__icontains=query) | Q(text__icontains=query)
)
messages.add_message(self.request, messages.INFO, query)
return queryset
~
上記のように paginated_byを使っている場合、検索一覧ページのテンプレート側で
~
{% if is_paginated %}
<ul>
{% if page_obj.has_previous %}
<li id="page-number">
<a href="?page={{ page_obj.previous_page_number }}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}">Previous</a>
</li>
{% endif %}
{% for num in paginator.page_range %}
{% if page_obj.number == num %}
<li id="page-number">
{{ num }}
</li>
{% else %}
<li id="page-number">
<a href="?page={{ num }}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li id="page-number">
<a href="?page={{ page_obj.next_page_number }}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}">Next</a>
</li>
{% endif %}
</ul>
{% endif %}
~
?page={{ num }}のページ数のURL生成部分に
{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}
のコードを追加することで、 [?page=2&query=検索クエリ] のようなURLを生成することができる。
##検索フォームをサイドバーなどに設置する場合
検索フォームをサイドバーなど詳細画面などの画面にも表示されている場合
上記の検索フォームだと、詳細画面から検索フォームに値を入れると/post/1/?query=検索ワード
となり、検索結果にリダイレクトされない。そのため、検索フォームの
action="URL"
を設定して一覧画面へ自動的に送られるようにする。以下のコードではaction="{% url 'post_list' %}"
として設定。
`template.html`
<div class="title">SEARCH</div>
<form action="{% url 'post_list' %}" method="get">
<input type="search" value="{{ request.GET.query }}" name="query" type="text" placeholder=" Input keywords..">
<button>Search</button>
</form>
まとめ
一覧画面に設置した検索フォームに値を入れ検索ボタンをおすと、検索ワードが含まれたpost_listが一覧表示されるようになった。
参考資料
https://zerofromlight.com/blogs/detail/59/#_1
https://docs.djangoproject.com/ja/3.1/ref/models/querysets/#django.db.models.Q
https://docs.djangoproject.com/ja/3.1/ref/contrib/messages/#django.contrib.messages.add_message
https://medium.com/@kjmczk/django-pagination-7c497995561e
更新
2020/12/21 検索結果の部分を追記
2020/12/29 検索結果の引き継ぎ方法を追記