読者です 読者をやめる 読者になる 読者になる

Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

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 にサーチボックスを作りたい

Components · Bootstrap

上記よりサーチボックスのサンプルコードを引っ張ってくる。

     <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

クエリを生成する — Django 1.4 documentation