Django開発~ブログ構築編6~django-tables2の続きです。
Blogもだんだんそれらしくなってきましたね。
本日は、投稿記事の「検索機能」をブログに搭載したいと思います。
postgresモジュールの追加
検索にPostgresの機能を使いたいので、settings.pyのINSTALLED_APPSに「django.contrib.postgres」を追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
vi app/app/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.postgres', # new # myapp 'blog', 'accounts', # sitemap 'django.contrib.sites', 'django.contrib.sitemaps', # django-tables2 'django_tables2', ] |
検索フォーム作成
まずは、blog/forms.pyに検索フォームクラスを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/blog/forms.py from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ('title', 'body', 'status', 'author') # new class SearchForm(forms.Form): search_key = forms.CharField( label='', max_length=128, widget=forms.TextInput(attrs={"class": 'float-right', 'placeholder': 'Search...'}), required=False) |
フリーテキスト入力ができる検索ボックスを一つ用意する予定なので、CharField要素を持ったクラス(SearchForm)を用意しました。
Viewの実装
作成したフォームをViewに搭載します。投稿リストを検索にかけたいので、post_list関数を修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
vi app/blog/views.py from django.contrib.auth import get_user_model from django.views.generic import CreateView, UpdateView, DeleteView from django.shortcuts import render, get_object_or_404, redirect from django_tables2 import RequestConfig from django.contrib.postgres.search import SearchVector # new from .models import Post from .forms import PostForm, SearchForm # new from .tables import PostTable # Update def post_list(request): posts = Post.published.all().filter(author__id=request.user.id) form = SearchForm() search_query = None if 'search_key' in request.GET: # 検索は、GETメソッドで行う(POSTでもできる), search_keyは、search.htmlに設定した名前 if request.GET['search_key']: form = SearchForm(request.GET) # リクエストをFormクラスに渡してイン スタンス化 if form.is_valid(): # formクラスを作成したら、is_validでバリデーションチェックする(必須) search_query = form.cleaned_data['search_key'] posts = Post.published.annotate( # 入力値がタイトルか本文に含ま れているかDB検索 search=SearchVector('title', 'body'),).filter(search=search_query) table = PostTable(posts) RequestConfig(request, paginate={'per_page': 3}).configure(table) return render(request, 'blog/post/list.html', { 'posts': posts, 'table': table, 'form': form}) |
ちょっと難しかったでしょうか。
これから作成するsearch.htmlから「search_key」という文字列と共に検索キーワードをDjangoに送信します。
GETで送信するのでリクエストがGET送信されているかチェックし、GETの場合は、SearchFormクラスをインスタンス化します。
リクエストから送られたデータを持つFormクラスが作成されるので、「is_valid」関数でデータの正常性を確認し、正常の場合は、cleaned_dataで検査済みの検索キーを取得します。
最後にこの検索クエリでDBに検索をかけます。検索対象は、「title」と「body」にしておきました。
検索ボックスの作成
最後に検索ボックス(search.html)を作成しましょう。
list.htmlに直接、検索ボックスを実装してもよかったのですが、分離しておくと他のテンプレートファイル内でも使いまわせます。
1 |
touch app/blog/templates/blog/search.html |
1 2 3 4 5 6 7 8 9 10 11 12 |
vi app/blog/templates/blog/search.html <form action="." method="get"> <div class="row"> <div class="col-10"> {{form.as_p}} </div> <div class="col-2"> <input type="submit" value="Search" class="btn btn-warning"> </div> </div> </form> |
form.as_pは、Djangoのフォームレンダリングオプションの一つです。フォームクラスから自動でHTMl要素を生成してくれます。
参考
後でブラウザのデベロッパーツールから確認いただくとわかると思いますが、formsが以下のように展開されます。
1 2 3 4 5 6 7 |
<form action="." method="get"> <p> <label for="id_search_key">Search key:</label> <input type="text" name="search_key" required id="id_search_key"> </p> <input type="submit" value="Search"> </form> |
inputタグに注目していただきたいのですが、name属性に「search_key」が設定されています。フォームレンダリングオプションを使用して作成したformには、forms.pyに設定した変数名(search_key)がname属性に設定されます。
そして、このname属性がGETリクエスト内に存在するかチェックすることで、検索フォームから送られてきたデータであるか判定するわけです。
list.htmlに検索ボックスを読み込ませましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
vi app/blog/templates/blog/post/list.html {% extends "blog/base.html" %} {% load django_tables2 %} {% block title %}My Blog{% endblock %} {% block content %} {% if user.is_authenticated %} <div class="row"> <h1 class="col-6">My Blog</h1> <a href="{% url 'logout' %}" class="btn btn-danger float-right col-2 mr-2 mb-2">Log Out</a> <a href="{% url 'blog:create' %}" class="btn btn-primary float-right col-2 mb-2">POST</a> </div> <div class="searchFormArea m-4"> {% include 'blog/search.html' %} </div> <div> {% render_table table %} </div> {% else %} <p>You are not logged in</p> <div class="row"> <a href="{% url 'login' %}" class="btn btn-primary col-2 mr-2">Log In</a> <a href="{% url 'signup' %}" class="btn btn-success col-2">SignUp</a> </div> {% endif %} {% endblock %} |
「{% include “htmlファイル名” %}」と指定すれば、他のhtmlファイルを読み込むことが可能です。
動作確認
「localhost:8000/blog」にアクセスして、動作検証をしてみましょう。
検索ボックスに「Add」と入力して、Searchボタンを謳歌して見ます。


「Add」に関連する記事が検索されましたね。次は、検索ボックスを空にしてからSearchボタンを押してみましょう。


検索ボックスが空の場合は、投稿データが全件表示されます。
検索機能は問題なく実装されたようです。
次回
次回は、レビュー機能を実装してみようと思います^^
コメントを残す
コメントを投稿するにはログインしてください。