django-filter の使い方（その２、クラスビュー編）

## はじめに
公式ページの一番簡単な例を参考に django-filter が動くまでを初心者向けに報告したいと思います。内容は大きく二つです。
* 関数ビューでの作り方
* クラスビューでの作り方

この（その２）では、後者のクラスビューでの作り方を紹介します。

なお、django-filter のインストールやモデル作成は
[django-filter の使い方（その１、関数ビュー編）](https://qiita.com/uqKen/items/453c314f364fe17194f6)
と同じです。そちらをご覧ください。
そして、関数ビューの作り方とごっちゃになりそうな方（私もでした）はプロジェクトを分けた方がよいかもしれません・・。
ちなみに私の環境ですが、
* python 3.8.6
* django 4.1.7
* django-filter 23.1

公式ページ
https://django-filter.readthedocs.io/en/stable/

前置きはこのくらいにしてルーティングから始めます。

## ルーティング（クラスビュー）
myapp内に urls.py を作成して以下を記述します。

```myapp/urls.py
from django.urls import path
from . import views
from myapp.models import Product

urlpatterns = [
    path("", views.FilterView.as_view(model=Product), name="product_list"),
]
```
通常のクラスビューと同じく as_view()を使用します。ここでの FilterView ですが、ListView を継承する形にしています。また、as_view()の引数には フィルターで使用するモデル(ここでは Product)を指定するように、と公式ページに書かれていました。
※公式ページに、from django_filters.views import FilterView とありましたが、これではうまくいきませんでした。

プロジェクトの urls.py も変更しておきます。これは関数ビューと同じです。
```[project]/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('myapp.urls')),
]
```

## フィルター定義ファイル（filters.py）の作成
次に、myapp 内に、filters.py の別ファイルを作ります。
このファイルも関数ビューと同じです。

```myapp/filters.py
import django_filters
from .models import Product 

class ProductFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_expr='iexact')
    class Meta:
        model = Product
        fields = ['price', 'release_date']
```

## クラスビュー(views.py)の定義
views.py に製品一覧(product_list)を表示するためのクラスビューを定義します。

```myapp/views.py
from django.views.generic import ListView
from .filters import ProductFilter

class FilterView(ListView):
    template_name = 'product_list.html'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["filter"] = ProductFilter(self.request.GET,
                queryset=self.get_queryset())
        return context
```
　当然ですが、関数ビューと大きく異なります。FilterViewは、ListViewを継承します。また、get_context_data()関数によって、検索キーワードのパラメータを受け取り、ProductFilter によって context内の"filter"をオーバーライドします。
　そして・・・データを絞り込む filter( ) メソッドはどこにも出てきません！

※ここで、from django_filters.views import FilterView として、FilterViewを継承する形をとってみましたが、うまく行きませんでした。 

## テンプレートファイル(product_list.html)の作成
myapp内に templates/myapp とディレクトリーを作って、product_list.html を作成します。関数ビューと同じ内容です。

```myapp/templates/myapp/product_list.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>製品一覧</title>
</head>
<body>
    <form method="get">
        {% csrf_token %}
        {{ filter.form.as_p }}
        <input type="submit" />
    </form>
    {% for obj in filter.qs %}
        {{ obj.name }} - ${{ obj.price }} {{ obj.release_date }}<br />
    {% endfor %}
</html>
```
そして、いつものように、
```
python manage.py runserver
```
を動かして、http://127.0.0.1:8000/にアクセスすると
![django-filter1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362730/ffd6667b-bba7-b2d2-1887-c87693dfdf60.jpeg)
検索フォーム付きで製品一覧が表示できました。パチパチパチ！
![django-filter3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362730/591c91da-c340-c63a-9ad7-e340d1da7e65.jpeg)
検索もバッチリですね。
## 最後に
　関数ビューとクラスビューに分けて、django-filter の使い方を見てきました。
私が django-filter による検索機能の実装がなかなかできなかった理由が、
* 通常のforms.py や objects.filter を使う方法と混同する
* django-filter の中でも関数ビューの実装とクラスビューの実装と混同する
* 基本を理解せず、いきなりDBから動的に取得する choisefield に挑戦して撃沈 

といった所にありました。Django フレームワーク全般がそうですが、初心者が少し道を外れるとエラー地獄になって挫折しまいます。ただ、これは大きな道筋を押さえていないからだ、と思っています。
　この記事が「検索機能の実装は苦手で面倒だな」と思っている方に少しでもお役に立てればと思います。プログラムも小さな成功体験からですから。
また、不備を指摘して頂けると助かります。まだまだ私も python初心者なので。