知識の枝

"All is well"

Django querysetで「or」を使う

約160日前 2021年6月19日13:49
デジタル
Django

改訂履歴


2021/6/19 投稿

1. 背景


Djangoを使った開発中に覚えたことを備忘録として残します。

今回はfilterを使ったモデルの絞り込みで「or」機能を使う方法を解説します。


2. 目的


「or」を使ってモデルを絞り込み、目的のアプトプットを得る。


3. はじめに


まずは使い方のイメージを共有します。

料理のレシピを提案するアプリがあったとしましょう。

アプリにはアプリ自体に元々登録してるレシピ(以降、グランドメニュー)があります。

また、ユーザーが任意にレシピを登録できる機能があります(以降、固有メニュー)。

レシピ一覧ページでは、利用しているユーザーに応じて表示するレシピを制限したいです。

具体的には、グランドメニューと自分が登録した固有メニューを表示します。




メニューは1つのモデルで管理しており、ユーザーと紐づけするフィールド「associated」を持ちます。

models.py
class Menu(models.Model):
associated = models.ForeignKey(Storage,
verbose_name='紐づいたユーザー',
blank=True,
null=True,
on_delete=models.CASCADE)
name = models.CharField(verbose_name='料理の名前', max_length=20)
calorie = models.IntegerField(verbose_name='カロリー', blank=True, null=True)

def __str__(self):
return str(self.name)
「associated」フィールドは「blank=True, null=True」としてあります。
「紐づいたユーザーが無い」=「グランドメニュー」とします。


レシピ一覧ページで表示する内容を絞り込むには、「グランドメニュー 又は Aさんの固有メニュー」という感じで「or」を使って絞り込むと良さそうです。

それではやり方を見ていきましょう。



4. 「or」で絞り込む


Djangoにはモデルを絞り込むための便利なメソッド「filter」があります。

使い方は以下のようなイメージです。
メニューのモデルから「Aさんに紐づいたメニューを抽出」する方法です。
menu_list = Menu.objects.filter(associated__userid=Aさんのid)


これに続けて「グランドメニューを抽出」すると絞り込みが「Aさんの登録したメニュー かつ グランドメニュー」 となってしまうので、「そんなものは無い!」とNoneが返されます。
menu_list = Menu.objects.filter(associated__userid=Aさんのid).filter(associated=None)
=> None #何も残らない



絞り込みは「かつ」では無く、「または」とする必要があります。


そこで使うのが「Qオブジェクト」です。

Djangoの公式ドキュメントに詳細が載っています。
Q オブジェクトを用いた複雑な検索


それでは使用例(今回の答え)を見ましょう。
from django.db.models import Q

menu_list = Menu.objects.filter(Q(associated__userid=Aさんのid) | Q(associated=None))
Q( )で囲った条件式2つがバーティカルバー「|」で区切られています。

これが「または」に相当する書き方です。

この機能を使う為には、views.pyの最初で「Q」をインポートしましょう。
from django.db.models import Q



見たままの使い方なので、特に詳しい解説はないです。



話は逸れますが、今回のような「メニューモデル」を構想した際に困った話があります。

グランドメニューと個別メニューのモデルを作成するとき、それぞれ「グランドメニューモデル」、「固有メニューモデル」として別々にモデルを作っていました。

今回のように絞り込みを行いhtmlで一覧表示させたかったのですが、モデルが異なる為「別々のモデル」としてhtmlテンプレートに呼び出す必要がありました。

html内のループ処理でメニューを一覧表示させますが、「グランドメニュー」用のループ処理と「固有メニュー」用のループ処理の2つを書く必要があり面倒くさかったです。

他にも「あるパラメーター(例えば登録日)」で並び替えを行うとき、別々のループ処理を行っているせいで思ったような並びになりませんでした。




どうしようかなと考えていたときに、ふと思いついたのが今回紹介したモデルの構造です。

グランドメニューか固有メニューかを区別するフィールドを作るだけでした。

思い返してみれば単純で簡単なことですが、それに気が付けて良かったです。

余談でした。



5. さいごに


地道にプログラミングを続けていけば、少しずつプログラミング脳になると思って精進します。

お疲れ様でした。