クエリの基本

基本のクエリ

世の中にあるほぼ全てのWebサービスがデータベースと連携してデータを扱っています。
そのデータベースに接続してデータの取得などの操作をすることをクエリと言います。

またPythonなどSQLではない言語を使用してデータを操作することをORMと言います。
ORMは、オブジェクト関係マッピングといい、英語ではObject-Relational Mapping と言います。

Djangoにも特有のORMが存在します。そのためDjangoのプロジェクト内ではSQLを使わなくても、Pythonの記法でデータベースにアクセスしデータを操作することが簡単にできます。

Djangoを使いこなす上でDjangoのORMは切っても切り離せない関係です。
まずは基本から見ていきましょう。

データの全取得

該当するテーブル(class)のデータを全取得するには、object.all()を使います。
以下のmodels.pyがあるとしましょう。

models.py


class Article(models.Model):

    title = models.CharField(max_length=100)
    body = models.TextField()
    is_published = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)


class Comment(models.Model):

    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)

存在する全てのArticleデータを取得したい場合、下記のようにすることでデータを取得することができます。

from .models import Article

articles = Article.objects.all()

データのソート

データを複数取得する際は、順番をソートすることも可能です。
この場合最後にorder_by()を加えます。

from .models import Article

articles = Article.objects.all().order_by('created')

order_by('created')を加えることで、データを古い順から取得することができます。
また値の先頭に-を加えることでソートの順を逆順にすることができます。
order_by('-created')とすることで、今度はデータを新しい順から取得することができます。

データのフィルタリング

データを全て取得したいケースは稀です。
多くの場合、フィルタリングをかけて、特定の要件に該当するデータのみを取得したいはずです。この場合objects.filter()を使います。

from .models import import Article

published_articles = Article.objects.filter(is_published=True)

これで、is_publishedがTrueのArticleのみ取得することができます。
フィルタリングは基本完全一致ですが、~以上や~未満、~を含むか、など様々な要件でフィルタリングをかけることができます。

__in

__inを使うと、指定するリストの中に該当するデータのみを引っ張ってくることができます。

from .models import import Article

sample_titles = ['test', 'sample_title']
published_articles = Article.objects.filter(title__in=sample_titles)

__gt, __lt

__gtはプログラミングの>に該当します。つまり指定する値より大きい場合にフィルタリングにヒットします。
__ltはプログラミングの<に該当します。つまり指定する値より小さい場合にフィルタリングにヒットします。

from .models import import Article

target_date = '2020-10-01'
published_articles = Article.objects.filter(created__gt=target_date)

これで2020年10月1日より新しいArticleのみ取得することができます。
__ltにすればtarget_dateより古いArticleのみを取得します。
またgtとltにそれぞれeを加えると同じ値も含むようになります。プログラミングでいう所の<=>=になります。

from .models import import Article

target_date = '2020-10-01'
published_articles = Article.objects.filter(created__lte=target_date)

全て英語の略称になってるので英語で覚えると良いでしょう。

  • gt
    greater than
  • gte
    greater than equal
  • lt
    lower than
  • lte
    lower than equal
フィルタリングのデータ作成

フィルタリング項目はあらかじめ辞書型で作成することができます。
通例、conditionという変数名を使用して、辞書型のデータを作成します。

あとはfilter(**condition)という形でデータを入れ込みます。

from .models import import Article

condition = {
    'created__gte': '2020-10-01',
    'is_published': True,
}
published_articles = Article.objects.filter(**condition)

これであらかじめ作成したフィルタリング項目を使用して、データを取得することができます。

クエリの応用

objects.all()objects.filter()で作成したクエリを応用することでデータの有無の確認や、データ数のカウントなどができます。

exists

exists()を使えば、そのデータが存在するときにTrue、存在しない場合はFalseを返すことができます。


from .models import import Article

condition = {
    'created__gte': '2020-10-01',
    'is_published': True,
}
article_exists = Article.objects.filter(**condition).exists()

例えばデータの中身は必要ないけど、データが存在するかどうかを確認したいときはexists()を使うと良いでしょう。

count

count()を使えば、そのデータの個数を確認することができます。


from .models import import Article

condition = {
    'created__gte': '2020-10-01',
    'is_published': True,
}
article_count = Article.objects.filter(**condition).count()

これもデータの中身は必要ないけど、該当するデータの個数を確認したいときに使用すると良いでしょう。

また、データの数の確認を行うだけでもデータベースに接続し、データを操作するアクションは行われます。
処理の最適化のために、データは取得しておきたいし個数も確認したい場合、Python関数のlen()を使用しましょう。


from .models import import Article

condition = {
    'created__gte': '2020-10-01',
    'is_published': True,
}
articles = Article.objects.filter(**condition)
article_count = len(articles)

これでクエリは1度で、データの内容も個数も取得することができます。


#### データの単体取得
`objects.all()`や`objects.filter()`は、クエリセット と言われるデータ型でデータを取得します。これは少しリストに似た形になっており例えデータが一つも存在しない場合でもプログラムを実行することができます。
これとは反対に単体のデータ(オブジェクト)を取得するのが`objects.get()`です。
`objects.get()`も`objects.filter()`と同じようにフィルタリングをかけることが可能です。
注意しなければいけないのは、仮にデータが1つもない場合、もしくは複数存在する場合その時点でエラーが返されることです。

Djangoのサービス運用をする上で、この類のエラーは頻発してしまうので注意して扱いましょう。
公式ドキュメントでは、必ずデータが1つしかないとわかっている時に使用しましょう。と書いてあります。

例
```.py
from .models import import Article

article = Article.objects.get(title='sample_title')

objects.all()objects.filter()はリストに似た型のクエリセット を取得するので実際はfor文やインデックス指定をして、データの中身を取り出します。


articles = Article.objects.all()
for article in articles:
    print(article.title)

print(article[0].title)

objects.get()では取得したそのままフィールド値を取り出すことができます。


article = Article.objects.get(title='sample_title')
print(article.title)

まずは、これら3つをうまく使いこなしていければ良いでしょう。