【ITニュース解説】Custom QuerySets in Django: Writing Cleaner, Reusable Queries
2025年09月20日に「Dev.to」が公開したITニュース「Custom QuerySets in Django: Writing Cleaner, Reusable Queries」について初心者にもわかりやすく解説しています。
ITニュース概要
DjangoのカスタムQuerySetは、重複しがちなデータベースクエリをまとめ、コードをクリーンに保つ仕組みだ。同じフィルタ処理を繰り返し書く手間を省き、DRY原則に則った保守しやすいアプリケーション開発を支援する。特定のデータ取得を効率化できる。
ITニュース解説
DjangoでWebアプリケーションを開発していると、アプリケーションの規模が大きくなるにつれて、同じようなデータベースクエリを何度も記述することが頻繁に発生する。たとえば、公開済みの記事だけを表示したい場合や、特定の著者による記事だけを取り出したい場合など、アプリケーションのさまざまな場所で同じ条件のフィルターを繰り返し適用することになる。このようなコードの繰り返しは、コードの保守を困難にし、全体の見通しを悪くする原因となる。このような問題に対し、Djangoのカスタムクエリセットが非常に有効な解決策を提供する。カスタムクエリセットを利用することで、ビジネスロジックの一部をモデルの近くにまとめ、コードの重複を避け、よりクリーンで再利用性の高いコードを書けるようになるのだ。
そもそも、Djangoにおけるクエリセットとは何だろうか。クエリセットは、DjangoのORM(Object-Relational Mapper)を通じてデータベースにクエリを発行するための、データベースクエリの集合体である。ORMは、データベースのテーブルをPythonのオブジェクトとして扱えるようにする仕組みであり、SQL文を直接書かなくてもデータベース操作を可能にする。例えば、Post.objects.all()と書けば、データベースに保存されている全ての記事(Postモデルのオブジェクト)を取得できる。また、Post.objects.filter(status="published")と書けば、ステータスが「published」(公開済み)の記事のみをフィルターして取得できる。ここで登場するobjectsは、Djangoがデフォルトで提供するマネージャーであり、基本的なクエリセットを返す。しかし、常に特定の条件でフィルターをかける必要がある場合、そのフィルター条件を毎回filter()メソッドで記述するのは、プログラムの世界で「Don't Repeat Yourself」(DRY)原則と呼ばれる「繰り返さない」という重要な考え方に反する。
では、実際にカスタムクエリセットをどのように定義し、利用するのかを見ていこう。まず、再利用したいクエリロジックをまとめるためのカスタムクエリセットクラスを作成する。これは、Djangoのmodels.QuerySetクラスを継承して作成する。たとえば、Postモデルがあり、公開済みの記事や下書きの記事、特定の著者による記事を取得するクエリを頻繁に利用すると仮定する。
1from django.db import models 2 3class PostQuerySet(models.QuerySet): 4 def published(self): 5 return self.filter(status="published") 6 7 def drafts(self): 8 return self.filter(status="draft") 9 10 def by_author(self, author): 11 return self.filter(author=author)
このPostQuerySetクラスでは、三つのカスタムメソッドが定義されている。published()メソッドは、自身(self)に対してstatus="published"という条件でフィルターを適用し、その結果のクエリセットを返す。同様に、drafts()メソッドはstatus="draft"でフィルターし、by_author(self, author)メソッドは引数で渡されたauthorに基づいて記事をフィルターする。これらのメソッドは、それぞれが特定のクエリ条件をカプセル化(ひとつのまとまりとして扱うこと)しているのだ。
次に、このカスタムクエリセットをPostモデルにアタッチ(関連付け)する必要がある。これにより、Post.objectsを通じてカスタムクエリセットで定義したメソッドが利用できるようになる。
1class Post(models.Model): 2 STATUS_CHOICES = ( 3 ("draft", "Draft"), 4 ("published", "Published"), 5 ) 6 7 title = models.CharField(max_length=200) 8 content = models.TextField() 9 status = models.CharField(max_length=10, choices=STATUS_CHOICES) 10 author = models.ForeignKey("auth.User", on_delete=models.CASCADE) 11 12 # カスタムQuerySetをアタッチ 13 objects = PostQuerySet.as_manager()
ここで重要なのは、objects = PostQuerySet.as_manager()という行である。この行によって、Postモデルのデフォルトのobjectsマネージャーが、先ほど定義したPostQuerySetに基づいて生成されたマネージャーに置き換えられる。as_manager()メソッドは、クエリセットクラスをマネージャーとして機能させるための変換を行うものだ。これにより、Post.objectsを通じてPostQuerySet内で定義されたpublished()やdrafts(), by_author()といったメソッドを呼び出せるようになる。
この設定が完了すると、クエリの記述は格段にシンプルになり、意図が明確になる。
1# 全ての公開済み記事を取得 2Post.objects.published() 3 4# 特定の著者による全ての下書き記事を取得 5Post.objects.drafts().by_author(user)
このように、Post.objects.published()と書くだけで、ステータスが「published」の記事が取得できるようになった。さらに、これらのカスタムクエリセットメソッドは、互いにチェーン(連結)して利用することも可能だ。
1# 特定の著者による公開済み記事を取得 2Post.objects.published().by_author(user)
この例では、まずpublished()メソッドで公開済みの記事をフィルターし、その結果に対してさらにby_author(user)メソッドで特定の著者による記事をフィルターしている。このようにクエリをチェーンすることで、複雑な条件でも簡潔かつ読みやすく記述できるのが大きなメリットだ。
ここで、カスタムクエリセットとカスタムマネージャーの使い分けについて触れておく。両者は似ているが、その用途には違いがある。カスタムクエリセットは、主に再利用可能なフィルター条件(例: .published(), .active())や、クエリをチェーンしたい場合に利用する。つまり、既存のクエリセットに対して特定の条件を追加していく用途に適している。一方、カスタムマネージャーは、get_queryset()メソッド自体をオーバーライドして、デフォルトで返されるクエリセットの挙動を変更したい場合や、クエリセット以外の結果(例えば、新しいオブジェクトの作成や、複雑な集計結果など)を返すクエリが必要な場合に利用する。今回の記事で紹介したas_manager()も、クエリセットをマネージャーとしてアタッチするために使われていたことを考えると、両者は密接に関連しつつも異なる役割を持つことが理解できるだろう。
まとめると、カスタムクエリセットは、Djangoアプリケーションにおいて、繰り返し発生するデータベースクエリを効率的に管理し、DRY原則を遵守するための強力なツールである。これにより、コードはよりクリーンになり、可読性と保守性が向上する。システムエンジニアを目指す初心者にとっても、このようなコードの整理術は、大規模なアプリケーション開発を円滑に進める上で不可欠なスキルとなるだろう。