【Django Rest Framework】Django-Filterを使ってフィルタ機能をカスタマイズする

# 概要

　この記事は初心者の自分がRESTfulなAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっております。

　前回の [Django Rest Framework で 特定のデータだけレスポンスするようにフィルタを設定する](https://qiita.com/Ajyarimochi/items/7e22de20292ca57ea8dc) で、 Generic Filtering を使って指定したフィールドのパラメータに一致するデータをフィルタリングできました。しかしこの方法では指定したパラメータに対して 「以上」、「以下」という条件でフィルタリングする事が出来ません。

そこで今回は日付について「〜より前」「〜より後」といった条件で有効期限を判定するフィルタ機能を実装します。

`Django-Filter`をインストールされていない場合は[前回の記事](https://qiita.com/Ajyarimochi/items/7e22de20292ca57ea8dc)を参考にインストールしてください。

### 参考

* [Django REST framework で django-filter を使う](https://qiita.com/okoppe8/items/77f7f91f6878e3f324cc)
* [Django-Filter 公式リファレンス](https://django-filter.readthedocs.io/en/latest/ref/filters.html#core-arguments)


### 環境

Mac OS 10.15
VSCode 1.39.2
pipenv 2018.11.26
Python 3.7.4
Django 2.2.6


# 手順

* views.py にFilterSetのクラスを作る
* 既存のViewSetでFilterSetを呼ぶ処理を追加 
* 試してみる

### views.py にFilterSetのクラスを作る

　django_filter の　FilterSetクラスを継承してオリジナルのフィルターセットを作ります。
まずFilterSetクラスをインポートするための処理を追加します。

```python

from django_filters import rest_framework as filters
```

次に、ViewSetのクラスの前にオリジナルのフィルターセットを実装します。

```python

class CustomFilter(filters.FilterSet):
    # フィルタの定義
    deadline = filters.DateFilter(field_name='deadline', lookup_expr='gte')

    class Meta:
        model = Coupon
        fields = ['deadline'] #定義したフィルタを列挙
```

下記の部分がフィルタの設定になります。今回はDateFilterを使いますが、他にも文字列のフィルターや数値のフィルターなど、沢山あります。詳しくは[公式ドキュメント](https://django-filter.readthedocs.io/en/latest/ref/filters.html#core-arguments)で！

```python

deadline = filters.DateFilter(field_name='deadline', lookup_expr='gte')
```

**補足**
上記の `field_name = ‘  ‘` は フィルタリングしたいモデルフィールドの名前を指定します。ここへ名前を指定しない場合はフィルタ定義の名前（上記のコードだと `deadline` の部分）がフィルタリング対象のモデルフィールドの名前として扱われます。

(サンプルコードには `field_name` を指定していますが、フィルタ定義の名前と同じであれば実は不要です）

リクエストする際のパラメータ名はフィルタ定義の名前になります。つまり、**リクエストする際のパラメータ名をモデルフィールドの名前と異なる名前にしたい場合は field_name の設定が必要**です。


下記の`model = ～`には、フィルタリングしたデータのモデル名を設定します。
`Fields = [～]`には、フィルタ定義の名前を列挙します。


```python

    class Meta:
        model = Coupon
        fields = ['deadline']
```


### 既存のViewSetでFilterSetを呼ぶ処理を追加

 上記で実装したフィルタクラスをfilter_classとして呼ぶだけです。

```python

filter_class = CustomFilter
```

### 試してみる
見積5分　→ 5分

リクエストパラメータとして、クーポンの有効期限を示す `deadline` を指定しない場合と、する場合で GETできるデータの違いを比較します。

**パラメータ deadline を指定しない場合**

リクエスト

```
curl -X GET http://127.0.0.1:8000/api/coupons/
```

レスポンス

[{"id":1,"code":"0001","benefit":"お会計から1,000円割引","explanation":"5,000円以上ご利用のお客様限定。他クーポンとの併用不可。","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":2,"code":"0002","benefit":"お会計を10%オフ！","explanation":"他クーポンとの併用不可","store":"有楽町店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":3,"code":"0003","benefit":"【ハロウィン限定】仮装して来店すると30%オフ","explanation":"全身の50%以上を仮装されているお客様限定（判断はスタッフの感覚とさせて頂きます）。他クーポンとの併用不可","store":"神田店","start":"2019-10-31","deadline":"2019-10-31","status":true},{"id":4,"code":"0004","benefit":"【9月限定】お月見団子サービス","explanation":"ご希望のお客様に月見団子をプレゼント！　他クーポンとの併用可能です！","store":"全店","start":"2019-09-01","deadline":"2019-09-30","status":true},{"id":5,"code":"0005","benefit":"【雨の日限定】お会計から15%オフ","explanation":"クーポンが配信された時だけ利用可能です。他クーポンとの併用不可","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":false},{"id":6,"code":"0006","benefit":"【日曜日限定】乾杯テキーラサービス","explanation":"テキーラを人数分サービスします。他クーポンとの併用可。","store":"神田店","start":"2019-11-03","deadline":"2019-12-01","status":true},{"id":7,"code":"0007","benefit":"お会計から19%引き","explanation":"１２月２９日～１２月３１日限定。他のクーポンとの併用不可","store":"神田店","start":"2019-12-29","deadline":"2019-12-31","status":true}]


**deadlineを指定する場合**

リクエスト（有効期限が12月5日以降のクーポンだけリクエスト）

```
curl -X GET http://127.0.0.1:8000/api/coupons/?deadline=2019-12-05
```

レスポンス

[{"id":1,"code":"0001","benefit":"お会計から1,000円割引","explanation":"5,000円以上ご利用のお客様限定。他クーポンとの併用不可。","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":2,"code":"0002","benefit":"お会計を10%オフ！","explanation":"他クーポンとの併用不可","store":"有楽町店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":5,"code":"0005","benefit":"【雨の日限定】お会計から15%オフ","explanation":"クーポンが配信された時だけ利用可能です。他クーポンとの併用不可","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":false},{"id":7,"code":"0007","benefit":"お会計から19%引き","explanation":"１２月２９日～１２月３１日限定。他のクーポンとの併用不可","store":"神田店","start":"2019-12-29","deadline":"2019-12-31","status":true}]



有効期限が12月5日以降のクーポンのみGET出来ました。

次は[認証機能を実装します](https://qiita.com/Ajyarimochi/items/c6274c4aa8f21f573265)