🚀

DjangoのモデルでQuerySetAPIの利用方法(基本)

2024/08/19に公開

DjangoのモデルでQuerySetのAPI

すぐに忘れてしまうので、モデルを利用したクエリ処理をを備忘録としてまとめておく

前提となるモデル

models.py
from django.db import models

class Monster ( models.Model ) :
  class Meta:
    db_table = "monster"
    
  TYPE_CHOICES = (
      (1, "炎属性"),
      (2, "水属性"),
      (3, "闇属性"),
  )

  name = models.CharField(
      max_length=100
  )

  hp = models.IntegerField(
      default=0
  )

  mp = models.IntegerField(
      default=0
  )

  type = models.IntegerField(
      choices=TYPE_CHOICES
  )

  birthday = models.DateField(
    verbose_name="birthday",
  )
  
  def __str__(self):
    return self.name

fixturesの作成

初期データを作成します

アプリ名/fixtures/monster.json
[
    {
        "model": "study.monster",
        "pk": 1,
        "fields": {
            "name": "FireDragon",
            "hp": 40,
            "mp": 20,
            "type" : 1,
            "birthday": "2000-05-12"
        }
    },
    {
        "model": "study.monster",
        "pk": 2,
        "fields": {
            "name": "IceDragon",
            "hp": 30,
            "mp": 30,
            "type" : 2,
            "birthday": "2000-07-20"
        }
    },
    {
        "model": "study.monster",
        "pk": 3,
        "fields": {
            "name": "DarkDragon",
            "hp": 100,
            "mp": 100,
            "type" : 3,
            "birthday": "2000-08-05"
        }
    },
    {
        "model": "study.monster",
        "pk": 4,
        "fields": {
            "name": "Slime",
            "hp": 10,
            "mp": 10,
            "type" : 2,
            "birthday": "2001-09-20"
        }
    },
    {
        "model": "study.monster",
        "pk": 5,
        "fields": {
            "name": "KingSlime",
            "hp": 80,
            "mp": 80,
            "type" : 2,
            "birthday": "2001-08-12"
        }
    },
    {
        "model": "study.monster",
        "pk": 6,
        "fields": {
            "name": "DarkKnight",
            "hp": 30,
            "mp": 60,
            "type" : 3,
            "birthday": "2001-04-10"
        }
    },
    {
        "model": "study.monster",
        "pk": 7,
        "fields": {
            "name": "FireGoblin",
            "hp": 50,
            "mp": 30,
            "type" : 1,
            "birthday": "2002-08-25"
        }
    }
]

マイグレーション

初期マイグレーションファイルを作成します

$  python manage.py makemigrations

Fixtureデータを登録するための空のマイグレーションファイルを作成します

$  python manage.py makemigrations アプリ名 --empty

マイグレーションファイルの修正

from django.db import migrations
from django.core.management import call_command


def load_fixture(apps, schema_editor):
    call_command('loaddata', 'study/fixtures/monster.json', app_label='study')


class Migration(migrations.Migration):

    dependencies = [
        ('study', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(load_fixture),
    ]

マイグレーション実行

$ python manage.py migrate

all

全件を取得する

Monster.objects.all()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`

filter

引数にカラム名を条件を指定して検索する

id=1を取得する

Monster.objects.filter(id=1)
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`id` = 1

name=FireDragonを取得する

 Monster.objects.filter(name="FireDragon")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`name` = 'FireDragon' 

HPが40以下、かつMPが20以上

# 複数を並べた場合、AND演算になる。この場合、HPが40以下、かつMPが20以上になる
Monster.objects.filter(hp__lte=40, mp__gte=20)
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    (`monster`.`hp` <= 40 AND `monster`.`mp` >= 20) 

exclude

与えられた条件にマッチしない結果セットを返します

id=1以外を取得する

# idが1以外を取得する
Monster.objects.exclude(id=1)
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    NOT (`monster`.`id` = 1) 

name=FireDragon以外を取得する

# nameがFireDragon以外を取得する
Monster.objects.exclude(name="FireDragon")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    NOT (`monster`.`name` = 'FireDragon') 

hpが40以下、かつmpが20以上ではないを取得する

# HPが40以下、かつMPが20以上以外を取得する
Monster.objects.exclude(hp__lte=40, mp__gte=20)
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    NOT (`monster`.`hp` <= 40 AND `monster`.`mp` >= 20)

get

1件を取得します。特定のモデルインスタンスを取得します。条件に一致したレコードが存在しない場合、エラーになります。条件に一致したレコードが複数県の場合、エラーになります

id=1を取得する

Monster.objects.get(id=1)
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`id` = 1

name=FireDragonを取得する

Monster.objects.get(name="FireDragon")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`name` = 'FireDragon'

ordder_by

並び替えます。引数でカラム名を指定します。昇順に並び替えます

idで昇順に並び替える

Monster.objects.order_by("id")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`id` ASC

hpで昇順に並び替える

Monster.objects.order_by("hp")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`hp` ASC 

mp, hpの昇順で並び替える

Monster.objects.order_by("mp","hp")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`mp` ASC,
    `monster`.`hp` ASC

reverse

降順に並び替えます。引数でカラム名を指定します

idで降順

Monster.objects.order_by("id").reverse()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`id` DESC

mp, hpで降順

Monster.objects.order_by("mp","hp").reverse()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`mp` DESC,
    `monster`.`hp` DESC

first

先頭の1件を取得します。戻り値はQuerySetではなく、モデルのインスタンスです。filter, order_by, reverseなどと併用することが可能です。該当するレコードがない場合、Noneを返します

hpが40以下をhpで昇順に並び替えた先頭の1件

Monster.objects.filter(hp__lte=40).order_by("hp").first()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`hp` <= 40
ORDER BY
    `monster`.`hp` ASC
LIMIT
    1

last

最後の1件を取得します。戻り値はQuerySetではなく、モデルのインスタンスです。filter, order_by, reverseなどと併用することが可能です。該当するレコードがない場合、Noneを返します

hpが40以下をhpで昇順に並び替えた最後の1件

Monster.objects.filter(hp__lte=40).order_by("hp").first()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`hp` <= 40
ORDER BY
    `monster`.`hp` DESC
LIMIT
    1

OR検索

WHERE句でのOR検索を行います。filterの場合、AND検索になります

from django.db.models import Q

hpが40以下、またはmpが20以上

Monster.objects.filter(Q(hp__lte=40) | Q(mp__gte=20))
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`hp` <= 40
OR
    `monster`.`mp` >= 20

範囲指定

範囲を指定した取り出します。Pythonのスライスを利用して特定の範囲を切り出します

hpで昇順に並び替えて、インデックスで2から4までを取得

Monster.objects.all().order_by("hp")[2:4]
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
ORDER BY
    `monster`.`hp` ASC
LIMIT
    2
OFFSET
    2

values

モデルのオブジェクトの結果セットではなく、ディクショナリ形式の結果セットで取得します。引数でカラム名を指定すると、特定のカラムだけ取得します

hpが80以上でディクショナリ形式で取得する

 Monster.objects.filter(hp__gte=80).values()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 80
QuerySet [
    {
        'id': 3,
        'name': 'DarkDragon',
        'hp': 100,
        'mp': 100,
        'type': 3,
        'birthday': datetime.date(2000, 8, 5)
    },
    {
        'id': 5,
        'name': 'KingSlime',
        'hp': 80,
        'mp': 80,
        'type': 2,
        'birthday': datetime.date(2001, 8, 12)
    }
]>

hpが80以上でid, name, hpをディクショナリ形式で取得する

Monster.objects.filter(hp__gte=80).values("id","name","hp")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 80
<QuerySet [
    {
        'id': 3,
        'name': 'DarkDragon',
        'hp': 100
    },
    {
        'id': 5,
        'name': 'KingSlime',
        'hp': 80
    }
]>

values_list

モデルのオブジェクトの結果セットではなく、タプル形式の結果セットで取得します。引数でカラム名を指定すると、特定のカラムだけ取得します

hpが80以上でタプル形式で取得する

Monster.objects.filter(hp__gte=80).values_list()
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`,
    `monster`.`mp`,
    `monster`.`type`,
    `monster`.`birthday`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 80
<QuerySet [
    (3, 'DarkDragon', 100, 100, 3, datetime.date(2000, 8, 5)),
    (5, 'KingSlime', 80, 80, 2, datetime.date(2001, 8, 12))
]>

hpが80以上でid, name, hpをタプル形式で取得する

 Monster.objects.filter(hp__gte=80).values_list("id","name","hp")
SELECT
    `monster`.`id`,
    `monster`.`name`,
    `monster`.`hp`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 80
<QuerySet [
    (3, 'DarkDragon', 100),
    (5, 'KingSlime', 80)
]>

exists

結果セットが存在するかどうかを真偽値で返します

id=1の存在確認

Monster.objects.filter(id=1).exists()
SELECT
    1 AS `a`
FROM
    `monster`
WHERE
    `monster`.`id` = 1
True

id=100の存在確認

Monster.objects.filter(id=100).exists()
SELECT
    1 AS `a`
FROM
    `monster`
WHERE
    `monster`.`id` = 100
False

name="xxx"の存在確認

 Monster.objects.filter(name="xxx").exists()
SELECT
    1 AS `a`
FROM
    `monster`
WHERE
    `monster`.`name` = 'xxx'

s

False

count

件数を取得します。数値が返ります

全件数の取得

Monster.objects.all().count()
SELECT
    COUNT(*) AS `__count`
FROM
    `monster`
7

hpが40以上の件数の取得

Monster.objects.filter(hp__gte=40).exists()
SELECT
    1 AS `a`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 40
True

aggregate

集計を行います。集計値(合計、平均など)をディクショナリ形式で返します

集計用の関数のインポート

from django.db.models import Count, Sum, Avg, Min, Max

全件の件数

この場合、countと同じですが、とりあえず、aggregateでも実現可能です

: Monster.objects.aggregate(Count("id"))
SELECT
    COUNT(`monster`.`id`) AS `id__count`
FROM
    `monster`
{'id__count': 7}

HPが40以上の件数

Monster.objects.filter(hp__gte=40).aggregate(Count("hp"))
SELECT
    COUNT(`monster`.`hp`) AS `hp__count`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 40
{'hp__count': 4}

HPが40以上の合計値

Monster.objects.filter(hp__gte=40).aggregate(Sum("hp"))
SELECT
    SUM(`monster`.`hp`) AS `hp__sum`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 40
{'hp__sum': 270}

HPが40以上の平均値

Monster.objects.filter(hp__gte=40).aggregate(Avg("hp"))
SELECT
    AVG(`monster`.`hp`) AS `hp__avg`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 40
{'hp__avg': 67.5}

HPの最大値

Monster.objects.filter(hp__gte=40).aggregate(Max("hp"))
SELECT
    MAX(`monster`.`hp`) AS `hp__max`
FROM
    `monster`
WHERE
    `monster`.`hp` >= 40
{'hp__max': 100}

Discussion