django-filter/DateFromToRangeFilterのsuffixを変更する

## 書くこと
[django-filter](https://github.com/carltongibson/django-filter)で用意されている`DateFromToRangeFilter()`を使うと、デフォルトでは指定したfieldのsuffixに`_after`と`_before`がついた物を拾ってその範囲で絞り込んでくれます。

ただ、今回自分は`_from`と`_to`を拾って範囲絞り込みをして欲しかったです。

少し時間がかかってしまったので、記録として記事にします。

[GitHub](https://github.com/donaisore/django-docker/tree/django-filter/custom_date_from_to_range)にもあげておきました。


## 準備
`created_at`カラムを持つ`BlogModel`を作って、そのlistを取得するエンドポイントを作成します。

```models.py
from django.db import models


class Blog(models.Model):
    title = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
```

```serializers.py
from rest_framework.serializers import ModelSerializer

from blogs.models import Blog


class BlogSerializer(ModelSerializer):
    class Meta:
        model = Blog
        fields = '__all__'
```

```filters.py
from django_filters import rest_framework as filters

from blogs.models import Blog


class BlogFilter(filters.FilterSet):
    created_at = filters.DateFromToRangeFilter()

    class Meta:
        model = Blog
        fields = ['created_at']
```


```views.py
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet

from blogs.filters import BlogFilter
from blogs.models import Blog
from blogs.serializers import BlogSerializer


class BlogViewSet(ViewSet):
    def list(self, request):
        queryset = Blog.objects.all()
        queryset = BlogFilter(data=request.GET, queryset=queryset, request=request).qs
        serializer = BlogSerializer(queryset, many=True)
        return Response(serializer.data)
```

```urls.py
from rest_framework.routers import DefaultRouter

from blogs.views import BlogViewSet

router = DefaultRouter(trailing_slash=False)

router.register(
    r'^blogs',
    BlogViewSet,
    basename='blogs'
)

urlpatterns = router.urls
```

最後に`2020年02月24日`にBlogテーブルにレコードを作成します。

```shell
>>> blog1 = Blog.objects.create(title='blog1')
<Blog: Blog object (1)>
>>> blog1.created_at
datetime.datetime(2020, 2, 24, 5, 54, 52, 859528, tzinfo=<UTC>)
```


これで`/blogs`にクエリパラメータ`created_at_after`, `created_at_before`を渡して範囲指定が出来るようになりました。

```shell
>>> curl 'http://localhost:18000/blogs?created_at_after=2020-02-01&created_at_before=2020-02-02'
[]

>>> curl 'http://localhost:18000/blogs?created_at_after=2020-02-01&created_at_before=2020-02-28'
[{"id":1,"title":"blog1","created_at":"2020-02-24T05:54:52.859528Z"}]
```

## 方針
今回したいことは、デフォルトで設定されているsuffixの`_after`, `_before`を変更するということです。

設定されているところを見つけて可能そうであればオーバーライドする等で対応しようと考えました。

`DateFromToRangeFilter`の中をのぞいてみます。
[DateFromToRangeFilter(GitHub)](https://github.com/carltongibson/django-filter/blob/master/django_filters/filters.py#L467-L468)

そうすると[DateRangeWidget(GitHub)](https://github.com/carltongibson/django-filter/blob/master/django_filters/widgets.py#L140-L141)でこのようにsuffixが設定されていることが分かりました。

```python
class DateRangeWidget(RangeWidget):
    suffixes = ['after', 'before']
```

これは[DateRangeField(GitHub)](https://github.com/carltongibson/django-filter/blob/master/django_filters/fields.py#L37-L60)で使われています。


以上から
1. CustomWidgetを作成
2. CustomDateRangeFieldを作成
3. CustomDateFromToRangeFilterを作成
のように実装することにしました。


## 実装
### 1. CustomWidgetを作成
suffixが`_from`, `_to`になるようにします

```widgets.py
from django_filters.widgets import DateRangeWidget


class CustomDateRangeWidget(DateRangeWidget):
    suffixes = ['from', 'to']
```

### 2. CustomDateRangeFieldを作成
CustomWidgetを使ってFilterに使用するためのFieldを作ります。

```fields.py
from django_filters.fields import DateRangeField

from blogs.widgets import CustomDateRangeWidget


class CustomDateRangeField(DateRangeField):
    widget = CustomDateRangeWidget
```

### 3. CustomDateFromToRangeFilterを作成
2で作った`CustomDateRangeField`を使ってFilterを作成します。

```filters.py
from django_filters import rest_framework as filters

from blogs.fields import CustomDateRangeField
from blogs.models import Blog


class CustomDateRangeFilter(filters.DateFromToRangeFilter):
    field_class = CustomDateRangeField


class BlogFilter(filters.FilterSet):
    # created_at = filters.DateFromToRangeFilter()
    created_at = CustomDateRangeFilter()

    class Meta:
        model = Blog
        fields = ['created_at']
```

もちろんデフォルトで用意されている`_after`, `_before`は使えなくなります。

確認しておきます。

```shell
_after,_beforeが使えないことを試す
>>> curl 'http://localhost:18000/blogs?created_at_after=2020-02-01&created_at_before=2020-02-02'
[{"id":1,"title":"blog1","created_at":"2020-02-24T05:54:52.859528Z"}]

>>> curl 'http://localhost:18000/blogs?created_at_after=2020-02-01&created_at_before=2020-02-28'
[{"id":1,"title":"blog1","created_at":"2020-02-24T05:54:52.859528Z"}]

_from, _toが期待通りに動作することを試す
>>> curl 'http://localhost:18000/blogs?created_at_from=2020-02-01&created_at_to=2020-02-02'
[]

>>> curl 'http://localhost:18000/blogs?created_at_from=2020-02-01&created_at_to=2020-02-28'
[{"id":1,"title":"blog1","created_at":"2020-02-24T05:54:52.859528Z"}]
```

以上でsuffixを変更することが出来ました。


## 最後に
今まで全く使ってこなかった`django-filter`ですが、非常に便利ですね。


「これもっと簡単に解決出来るよ」というのをご存知の方がいれば教えていただけると嬉しいです。

__ありがとうございました！__