matobaの備忘録

育児しながら働くあるエンジニアの記録

django-filterを軽く動かしてみる(DRF編)

昨日、django-filterを動かした記録を書きました。 次は、DRFで利用する前提で動かしてみた記録を書きます。

前提

以下、記事から続く形で動かしていきます。

blog.mtb-production.info

前回の記事にも書いたことと重複しますが、基本的にドキュメントに書いてあることを斜め読みしながら動かしつつ、気になった部分のドキュメントやソースを僕が読んで記録していく記事です。間違っている可能性はありますので、より確かな情報は各自ドキュメントやソースを読んでください。

DRFで使う

そもそもDRFとフィルター

そもそも、DRFには、FilterBackendという概念がある。

Filtering - Django REST framework

ドキュメントをざっと読みしたところ、次の経緯で作られたと考えると、自分にとってはわかりやすいと感じました。(あくまで私の解釈です)

  • DRFで一覧を取得するAPIを作る場合、ListAPIViewを使う
  • 絞り込みを実装したい場合は、 get_queryset() に実装する
  • ただ、これだと次のような時に困る
    • 複数のListAPIViewで共通化したFilterを使い回したい時
    • 既存の検索や絞り込みを利用したい時
  • というわけで、次の対応を行なった
    • DRFでは、絞り込み処理部分をクラスとして定義できるようにした。これをFilterSetと呼び、定義方法やページネーションとの差異を吸収するためにFilterBackendができた。

django-filterはDRFのFilterBackendに使える形式になっていますが、django-filter以外のもDRFで利用できるFilterがあるようです。

DRFドキュメントの末尾で以下のものが紹介されていました。

  • django-filter
  • django-rest-framework-filters
  • djangorestframework-word-filter
  • django-url-filter
  • drf-url-filter

今回は、django-filterを動かしてみます。 今後、気が向いたら動かしてみたいと思います。

DRF側でdjango-filterを導入する前の状態

とりあえずdjango-filterを導入する前に、DRF側でAPIを作っておきます。

serializersに以下を追加します。

from rest_framework import serializers

from myapp.models import Product

class ProductSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Product
        fields = ["name", "price", "description", "release_date"]

viewsに以下を追加。

from rest_framework import viewsets
from myapp.models import Product
from myapp.serializers import ProductSerializer

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

urls.pyに以下を追加。

from django.urls import path, include
from rest_framework import routers

from myapp.views import ProductViewSet

router = routers.DefaultRouter()
router.register(r"products", ProductViewSet)

app_name = "myapp"
urlpatterns = [
    path("", include(router.urls)),
]

この状態でブラウザブルAPIを確認して表示されることを確認しておく。

f:id:mtb_beta:20210807195359p:plain

DRF側にFilterBackendを設定する

ListAPIViewの絞り込み結果に、FilterSetを利用するためにはViewに以下の差分を追加します。これで、有効化されます。

@@ -1,5 +1,6 @@
 from django.shortcuts import render
 from rest_framework import viewsets
+from django_filters import rest_framework as filters
 from myapp.filters import ProductFilter
 from myapp.models import Product
 from myapp.serializers import ProductSerializer

...

 class ProductViewSet(viewsets.ModelViewSet):
     queryset = Product.objects.all()
     serializer_class = ProductSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filterset_class = ProductFilter

この状態で、ブラウザブルAPIを開くと、Filterボタンが追加されてました。

f:id:mtb_beta:20210807200204p:plain

モーダルでパラメータを渡せるようになって動作確認ができました。

f:id:mtb_beta:20210807200311p:plain

簡単ー!!

あとは、Djangoの時と同じくFilterクラスを修正すれば検索クエリを実装できそうです

なお、django-filterのFilterSetをDRFで利用するためには、Viewに filter_backends = (filters.DjangoFilterBackend,) を指定する必要があるのですが、settingsで以下のように書いておくと、プロジェクト共通の設定として、済ませることができるとのことです。

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        ...
    ),
}

おまけ

DRFにとってFilterBackendの指定はなぜ必要か

DRFの仕組みを調査している際に、以下のようなことが気になりました。

  • DRFにとってFilterBackendの指定はなぜ必要か?
  • FilterBackendの処理で何をしているのか?

というわけで、次の二点が知りたくて、実装を調査してみました。

  • DRF側のFilterBackendの読み込み実装と経緯など詳細
  • django-filterのFilterBackendの実装など詳細

すると、まず、FilterBackendでページネーションやSchema自動生成との兼ね合い、FilterSetクラスなどを処理してるというのはわかりました。

また、元々、django-rest-frameworkでDjangoFilterBackendと呼ばれる処理を持っていたが、途中で非推奨となり、django-filterのDjangoFilterBackendが推奨となったという、雑学が手に入りました。

github.com

終わり

とりあえずdjango-filterをDRFで動かした記録を書きました。

DRFのfilter拡張のライブラリはいくつかあるみたいなので、また気が向いたら動かしてみたいと思います。

あと、関連で以下のライブラリも見つけたので、こちらも気が向いたら動かしてみたいところ。

github.com

リースとサブスクリプションって何が違うの?と思った話

ふと、次のような疑問が頭をよぎった。

  • リースとサブスクリプションって何が違うの?

Webサービスのサブスクリプションはよく聞くけど、リースとは言わない。

何となくビジネスの仕組みが違うような気がしたので、本を買ってざっと読んでみた。備忘録的に書いておく。

続きを読む

何度も説明しないためにブログに書く

「何度も説明しないためにブログに書く」という考えを思い出した。

思い出したついでに備忘録に書いておく。

書くのが億劫?

以前はブログに記事を書いていたけど、最近は書くことが減った。なんとなく、書くのが億劫になったから。

なぜ、書くのが億劫になったのかはいまいち言語化できていなかったけど、書いている途中で「仕上げるのがめんどくさい」という気持ちになってしまうのはわかっている。

だから厳密にいうと「書くのが面倒」ではなくて「書いた記事を公開することが億劫」という話のように思う。

勉強会に参加して気づいた

昨日、ゆるい雑談の勉強会に参加した。

cokonpile.connpass.com

で、ゆるゆると2時間ほど雑談をしていたのだけど、雑談をしている中で自己紹介をしたり聞かれたことに答えたりしていた。

この中で「ああ、そういえば、僕は何度も説明しないためにブログに記事を書いていたのだなあ」ということを思い出した。

よく聞かれることを記事に書いていれば、話している中で説明が必要になったらペタッとリンクを貼れる。これが便利だったんだな。

ついでに思い出した

ついでに、思い出したこととして、何度も調べないためにブログに書くというのもあったのを思い出した。

技術的なことを雑談的に他の人に話しているとき、自分が喋ろうとしている内容を念のため確認するため、再度調べたりする。

そのときに、辿りやすくするために記事を書いていたことがあったのも思い出した。

人と話す回数が減るとブログ記事が減る

ここから気づいたことが人と話す回数が減ると執筆するブログ記事が減るということ。

コロナ禍になってから圧倒的に新しい人と話す回数が減っている。

新しい人と話すことが減ると、自分は知っているけど他の人は知らないことが多い話を何度も説明することが減る。

僕は、「自分は知っているけど他の人は知らないことが多い話」を記事に書いたりするので、ブログの記事が減っていたのもあるんだろうな、と思った。

FreenoveのRaspberry Piスターターキットを買った話

以前から、ラズパイに興味があった。なんとなく触ってみたいなあ、というレベルの興味。

「興味がある」と言いつつ触っていない状態が続いてることを自分で認識すると、なんとなく自分にイラッとしてくる。なので、今回はとりあえず入門キットだと思われるものを購入した。触る時間作れるのかなあ、このメーカーで大丈夫かなあ、とかいろいろ思うことはあるが、ここは深く考えないことにした。

購入したものはこちら。

Freenove Raspberry Pi 4 B 3 B+ 400用の究極のスターターキット、434ページの詳細なチュートリアル、Python C Javaコード、223アイテム、57プロジェクト、無はんだブレッドボード

PythonとJavaとCのコードなので、なんとなくとっつきやすい印象があった。

そして、購入したものが届いた。

f:id:mtb_beta:20210723100527j:plain

この製品は、マニュアルがWebからダウンロードする形式になっている。

こんなふうにAmazonにリンクがついている。

f:id:mtb_beta:20210723095826p:plain

「変なファイルをばらまかれるリンクだったらどうしよう?」という防衛意識が働いて、少し抵抗があった。

実際にダウンロードすると、中にはサンプルコードやチュートリアルが入っている。

というか実際に入ってるファイルはGitHubで公開されている。

じゃあ最初からGitHubのリンク貼ってくれ....!!!と思ったりもした。

github.com

それからリポジトリの中にはPythonのサンプルコードがたくさん入っていた。

Pythonのコードをいくつかみてみたが、「読める...!!これはらわかるぞ...!!!」みたいな気持ちになった。

まだ、大量のチュートリアルがあるので、まずはざっと把握してみるところかなあ、と思いつつ楽しみにしている。