ViewSetは4種類ある
Django REST FrameworkのViewSetには以下の4種類があります。
- 基本のViewSet
- GenericViewSet
- ModelViewSet
- ReadOnlyModelViewSet
それぞれ異なるレベルの機能を持っています。
参考:
基本のViewSet(カスタムViewSet,シンプルViewSet)
基本的なViewSetクラスです。APIView(カスタマイズ可能なAPIエンドポイントを作るためのベース)から継承され、通常の属性(permission_classesやauthentication_classesなど)を使用してAPIポリシーを制御できます。
このクラス自体はアクションの実装を持っていませんが、APIポリシー(認証や権限管理)を制御するための属性を持ちます。ユーザーがクラスを拡張し、アクションを実装する必要があります。
1番サンプルで、何にでも対応できるけど、全部自分でやらなければいけないので、コードが冗長になり開発効率が悪く、運用性も低くなるので、可能な限り以下の3つのViewSetを使った方がいいでしょう。
GenericViewSet
ViewSetを拡張したクラスで、get_querysetやget_objectのような汎用的なメソッドを提供します。デフォルトではアクションはありませんが、ミックスインを追加してアクションを定義できます。
ModelViewSet
GenericViewSetに様々なミックスインを追加し、標準的なCRUD操作(list()、retrieve()、create()、update()、destroy()など)を提供するクラスです。完全なCRUD操作が必要な場合に便利です。
ReadOnlyModelViewSet
ModelViewSetの簡易版で、読み取り専用(list()とretrieve())のアクションのみを提供するクラスです。データを閲覧するためのAPIを構築する際に使用します。
4種類のViewSetの使い分け方
いろいろあって使い分けるのが面倒なので、全部基本のViewSetでいいのでは?と最初は思ったのですが、ModelViewSetやGenericViewSetが提供している便利な標準メソッドを使わないため、毎回全てを手動で実装する必要が生じます。開発効率が低下し、冗長なコードが増えてしまって、運用性上も不都合が生じることになります。
そこで使い分けが必要になるのですが、まとめると以下のように使い分けるとよさそうです。
- 標準的なCRUDが必要 → ModelViewSet
- 読み取り専用 → ReadOnlyModelViewSet
- 一部の操作だけ必要 → GenericViewSet+mixinで必要な操作だけを実装。
- 完全にカスタムアクション → 基本のViewSetを使って全て自分で定義。
ViewSet (基本のViewSet)
ViewSetは非常に柔軟で、必要に応じてアクションを自分でカスタマイズできます。
しかし、すべてのアクション(list, create, retrieve, update, destroyなど)を手動で実装しなければならないため、コードが冗長になりやすいです。
特に、標準的なCRUD操作が必要な場合は、他のViewSetを使う方が効率的です。
使いどころ
すべてのアクションをカスタマイズしたい場合や、標準的なCRUD以外の動作が必要な場合
デメリット
一から全てのアクションを実装する必要があるため、開発コストが高くなる。
GenericViewSet
GenericViewSetは、標準的なget_querysetやget_objectといった便利なメソッドが提供されます。特に、mixinを組み合わせて使うことで、必要なアクションだけを追加できます。
例えば、リスト表示のみが必要ならListModelMixinだけ追加する、といった具合です。
ModelViewSetはCRUD操作ができますが、CRUD操作のすべての機能を必要としない場合はGenericViewSetが適しています。
使いどころ
カスタマイズしたアクションが必要だけど、完全にゼロからではなく標準的なメソッドを活用したい場合。
デメリット
mixinを適切に選んで組み合わせる手間がある。
ModelViewSet
ModelViewSetは、CRUD操作がすべてデフォルトで実装されています。
そのため標準的なCRUD操作を行うならこクラスを使うことで、簡単にCRUD操作を提供でき、開発効率が高いです。
使いどころ
標準的なCRUD操作が必要な場合。特に、リスト表示や詳細取得、作成、更新、削除の操作がすべて必要な場合。
デメリット
必要以上のアクション(例えば、削除が不要でもdestroy()が定義される)が提供されるため、過剰な機能が含まれることがある。
ReadOnlyModelViewSet
ReadOnlyModelViewSetは、読み取り専用のAPIに適しています。
データのリスト表示や詳細取得だけが必要な場合に使います。
更新や削除のアクションがないため、セキュリティ的に読み取り専用のAPIが求められる場面で便利です。
使いどころ
データの閲覧のみを許可するAPIが必要な場合。例えば、データを参照するユーザーに対して書き込み操作を制限したい場合。
デメリット
更新や削除が必要な場合には適していない。
GenericViewSet vs ModelViewSet
GenericViewSetとModelViewSetの使い分けが難しいと感じています。
どちらも実装方法に違いはあれど基本的には同じことができ、開発者の好みやプロジェクトの要件次第ですが、CRUDをすべてカバーしたい場合や、標準的な操作を提供したい場合にはModelViewSetの方が便利で、ほとんどのケースで推奨されるようです。
しかしカスタムなアクションや部分的な機能が必要な場合には、GenericAPIViewやGenericViewSetを使う柔軟性が役立つこともあります。
stackoverflowにもDjango REST Framework: Generics or ModelViewSets?という質問があったのでこれらの意見も参考にしつつ補足も追加してまとめようと思います。
GenericViewSet
GenericViewSetの利点は必要なアクションのみを実装するという軽量さです。
例えば、特定のAPIが完全に読み取り専用で、データの作成や削除が今後も不要であることが確定している場合、GenericViewSetでシンプルにRetrieveとListのみを提供する選択も正当化されます。
ポイントは拡張性を持たせる必要があるかどうかだと思います。CRUDのうち、最初は1、2個の機能しか必要ではなくても、後々他の機能追加がありえるなら、最初からModelViewSetにしておいた方が柔軟に対応できます。しかし、最初から機能拡張がなされる可能性がないのであればGenericViewSetは無駄な機能を省いていて良い選択だと言えそうです。
GenericViewSetの使用例としては、例えば記事一覧(リスト表示)APIを作成し、フロントエンドがデータのレンダリングや表示処理を担当する場合はGenericViewSet + ListModelMixinを使うと実装がシンプルになります。
ユースケース
- シンプルにデータベースからデータを取得して、そのままクライアントに返すAPIを作りたい。
- データの取得に特化し、追加のロジックや体裁を整えることはフロントエンドに任せる。
ModelViewSet
ModelViewSetは、GenericAPIViewを継承しており、さらに、list()、retrieve()、create()、update()、destroy()といった基本的なCRUD操作が最初から備わっています。
例えばサーバー側でフィルタリングや集計処理、フォーマット変換など、データを整形してクライアントに返す場合はModelViewSetが適しています。
ユースケース
- データの取得に加えて、サーバーサイドでデータを加工したり、複雑な処理を加えたい場合。
- デフォルトでCRUD操作が提供されているため、カスタマイズしやすい。例えばlist()メソッドをオーバーライドして、表示用の体裁を整えるフォーマット処理も行える。
また、ModelViewSetは、Django REST Framework (DRF) のルーター機能を使ってURLパターンを自動的に生成できるため、利便性が高いです。一方、Generics(汎用クラス)では、URLパターンを手動で定義する必要があります。
ModelViewSetを使うと、CRUD(Create、Read、Update、Delete)操作をすべて1つのクラスで処理できるため、コードが短くなります。
Genericsを使う場合は、リストと作成に対応するクラス(ListCreateAPIView)と、取得・更新・削除に対応するクラス(RetrieveUpdateDestroyAPIView)の2つのクラスが必要になります。
同じことは出来ても、コードが冗長になり、運用性が落ちる可能性が上がります。
結論:将来の拡張性が判断のポイントとなる
CRUD操作の全体的なサポートを視野に入れている場合、最初からModelViewSetを使う方が一貫性があり、将来的な変更や機能追加にも対応しやすいです。コードの再利用性が高く、開発コストも抑えられます。
ただし局所的な最適化が必要ならGenericViewSet
特定のAPIが限定された機能しか提供しない、またはその提供する機能が固定されている場合には、GenericViewSetを使うことで無駄を省くことも考えられます。