Django e-commerce part18 Search Query
サーチ機能をつける
products/views.py を編集する。
class ProductListView(ListView): ... def get_queryset(self, *args, **kwargs): qs = super(ProductListView, self).get_queryset(*args, **kwargs) return qs ...
これは、get_queryメソッドを上書きするコードになる。
さらに、これを修正していくと、
from django.db.models import Q #Q をimport ... def get_queryset(self, *args, **kwargs): qs = super(ProductListView, self).get_queryset(*args, **kwargs) query = self.request.GET.get("q") if query: qs = self.model.objects.filter( Q(title__icontains=query) | Q(description__icontains=query) ) return qs ...
query = self.request.GET.get("q")
の意味は、views.ProductListView().request のGETメソッドで、取得したq をqueryに代入するという意味??
例えば、ブラウザのURL欄に、products/?q=〇〇
と入っている場合、〇〇を qに代入するということになるため、query="〇〇" になる。
self.model.objects.filter は、ProductListView().model(Product).objects.filter()という意味なので、要は、productのobjectsにフィルタをかける。
Q()は、複数の条件で、絞込みができる?Q lookups と言っていた。
クエリを生成する — Django 1.4 documentation
なぜQが必要かというと、
qs = self.model.objects.filter(title__icontains=query)
だと、1つしか絞り込み条件を設定できないため。
詳しくは公式ドキュメントを参照
def get_context_dataのcontextに、以下のように追加してみる。
context["query"] = self.request.GET.get("q")
これは、"query": q というcontextを渡すという意味になります。qは、ブラウザの?q=〜〜 の、〜〜のところ。なぜこの設定が必要かというと、検索したワードを表示するためか。
navbar にサーチボックスを作りたい
上記よりサーチボックスのサンプルコードを引っ張ってくる。
<form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form>
まず、formタグを修正する。 * methodを追加(サーチボックスのため、GET) * role="search" * actionの宛先は、productlistviewのurlのnameを指定する。
<form class="navbar-form navbar-left" method="GET" role="search" action="{% url 'product_list'%}">
次に、input タグを修正していく。 * name = "q"を指定する。(これは、ブラウザの検索ボックスの、?q=の q だと思う。)
<input type="text" class="form-control" placeholder="Search" name="q">
完成系の見本はこんな感じ。
<form class="navbar-form navbar-left" method="GET" role="search" action="{% url 'product_list'%}"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" name="q"> </div>
ちなみにsubmitボタンは消してOK。
priceなどの数字関連の検索をしたい場合
文字列と、decimalフィールドを一緒の検索条件に入れておくと、文字列で検索しようとした時にエラーが出る。 そのため、以下のように、一度文字列の検索条件を入れておき、その後に、try文で、価格を検索する。 そのあと、distince()で、qs,qs2の重複を削除する。
def get_queryset(self, *args, **kwargs): qs = super(ProductListView, self).get_queryset(*args, **kwargs) query = self.request.GET.get("q") if query: qs = self.model.objects.filter( Q(title__icontains=query) | Q(description__icontains=query) ) try: qs2 = self.model.objects.filter( Q(price=query) ) qs = (qs | qs2).distinct() except: pass return qs
(qs | qs2).distinct() は、qs, qs2 の重複を削除するmethod