Djangoでのデータベースの扱い方
Oracle Master 12c Bronze SQL基礎の資格取得に向け日々精進しているhashiです。
今回は自分が過去に扱ったDjango(ジャンゴ)というPythonのWebアプリケーションフレームワークでのデータベース操作を行う主なクエリと、それと等価なSQL文を併記して、知識を整理することを目的とします。
動作環境
テーブルの生成
Djangoでのテーブル定義は以下のように記述します。
クラス名がテーブル名、クラス変数の左辺が列の名前、右辺に型の情報やオプションを記述しています。
"""models.py""" from django.db import models class UserTable(models.Model): """ユーザー情報のテーブル""" user_id = models.IntegerField("ID", primary_key=True) name = models.CharField("ユーザー名", max_length=30) age = models.IntegerField("年齢", blank=True, null=True) class PhoneTable(models.Model): """ユーザーの持つ電話のテーブル""" user = models.ForeignKey(UserTable, on_delete=models.CASCADE) tel = models.CharField("電話番号", max_length=15) email = models.EmailField("メールアドレス") contract_date = models.DateField("契約日", blank=True, null=True)
上のように記述した後、「データベースマイグレーション」という操作を行うとデータベース上にテーブルが作られます。
vsCodeの拡張機能であるSQLite
を用いてデータベースの情報を確認すると……
UserTable
列 | 型 | NULLを許容する | 制約 |
---|---|---|---|
user_id | integer | NOT NULL | PRIMARY KEY |
name | varchar(30) | NOT NULL | |
age | integer |
PhoneTable
列 | 型 | NULLを許容する | 制約 |
---|---|---|---|
user | integer | NOT NULL | REFERENCES |
tel | varchar(15) | NOT NULL | |
varchar(254) | NOT NULL | ||
contract_date | date |
このように、ユーザー情報と携帯電話の情報という2つのテーブルが作られます。
テーブルからのオブジェクトの取得
Djangoでは、データベースからオブジェクトを取得する際に「クエリセット」を作ります。
Djangoのドキュメント(https://docs.djangoproject.com/ja/2.2/topics/db/queries/)によれば、
データベースからオブジェクトを取得するには、モデルクラスの Manager から QuerySet を作ります。 QuerySet はデータベース上のオブジェクトの集合を表しています。多数の フィルター を持つことができます。フィルターは与えられたパラメータに基づいてクエリの検索結果を絞り込みます。SQL 文においては、 QuerySet は SELECT 句、フィルターは WHERE や LIMIT のような絞り込みに用いる句に対応しています。
とあります。実際に絞り込みを行ってみます。
先ほど作ったテーブルに以下のようなデータを追加しました。
これからの操作はこのデータを用いて行います。
UserTable
user_id | name | age |
---|---|---|
1 | tanaka | 34 |
2 | sato | |
3 | suzuki | 40 |
4 | sasaki | 24 |
PhoneTable
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
1 | 0120-976-543 | testtest@t.com | |
1 | 0120-456-456 | karikari@test.com | |
4 | 456 | poi@poi.jp |
全データの取得
phones0 = PhoneTable.objects.all() #全データの取得
PhoneTableクラスのManagerであるobjectsにall() メソッドを呼ぶことで全データを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable;
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
1 | 0120-976-543 | testtest@t.com | |
1 | 0120-456-456 | karikari@test.com | |
4 | 456 | poi@poi.jp |
完全一致検索
取得するオブジェクトの絞り込みを行う際はfilter()メソッドを利用します。引数に入れる条件を変えることで検索条件を変えることができ、
phones1 = PhoneTable.objects.filter(tel='0120-456-456') # 完全一致検索
列名=値
を引数にすることでPhoneTableのtel列が0120-456-456
に等しいデータのみを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE tel='0120-456-456';
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
1 | 0120-456-456 | karikari@test.com |
部分一致検索
完全一致だけでなく部分一致も可能です。
phones2 = PhoneTable.objects.filter(tel__contains='456') # 部分一致検索
列名__contains=値
を引数にすることでPhoneTableのtel列に456
を含むデータのみを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE tel LIKE '%456%';
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
1 | 0120-456-456 | karikari@test.com | |
4 | 456 | poi@poi.jp |
リスト中からの検索
複数の値のどれかと等しいデータの取得が可能です。
# リスト中からの検索 phones3 = PhoneTable.objects.filter(tel__in=['0120-456-456', '0120-999-999'])
列名__in=[値1,値2…]
を引数にすることでPhoneTableのtel列がリストのどれかと等しいデータを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE tel IN ('0120-456-456', '0120-999-999');
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
1 | 0120-456-456 | karikari@test.com |
○○未満のものを検索
# 比較対象未満(日付なら過去)を検索
phones4 = PhoneTable.objects.filter(contract_date__lt=datetime.date.today())
列名__lt=値
を引数にすることでPhoneTableのcontract_date列がdatetime.date.today()
(=2019年7月5日)より前のデータのみを取得できます。(NULLとの比較はできない)
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE contract_date < SYSDATE;
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
○○より大きいものを検索
# 比較対象より大きいもの(日付なら未来)を検索
phones5 = PhoneTable.objects.filter(contract_date__gt=datetime.date.today())
列名__gt=値
を引数にすることでPhoneTableのcontract_date列がdatetime.date.today()
(=2019年7月5日)より後のデータのみを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE contract_date > SYSDATE;
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
NULL判定
# NULLの判定 phones6 = PhoneTable.objects.filter(contract_date__isnull=True)
列名__isnull=True
を引数にすることでPhoneTableのcontract_date列がNULLのデータのみを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE contract_date IS NULL;
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-976-543 | testtest@t.com | |
1 | 0120-456-456 | karikari@test.com | |
4 | 456 | poi@poi.jp |
外部キーで検索
# 外部キーで検索 temp = UserTable.objects.get(age=34) phones7 = PhoneTable.objects.filter(user=temp)
外部キーを条件とした検索も可能です。上では2段階に分けて検索を行っています。
1行目でget()を使用している理由ですが、filter()では検索結果が1つの場合でも返り値の型がリストになってしまうためです。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE (SELECT user_id FROM UserTable WHERE age = 34) = 1;
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
1 | 0120-456-456 | tanaka@t.com | 2018年10月1日 |
1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
1 | 0120-976-543 | testtest@t.com | |
1 | 0120-456-456 | karikari@test.com |
and検索
# and検索 phones8 = PhoneTable.objects.filter(tel="0120-999-999", email="test@t.com")
filter()の引数に複数の検索条件をカンマで区切って入れることで、そのすべてに当てはまるデータを取得できます。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE tel='0120-999-999' AND email='test@t.com';
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
or検索
from django.db.models import Q # or検索 phones9 = PhoneTable.objects.filter(Q(tel="0120-999-999") | Q(email="phone@t.com"))
or検索は少し特殊で、Qオブジェクトというものを使って実現しています。Q(条件1) | Q(条件2)
という形式になります。
SQL文で表すとこのようになるでしょう。
SELECT * FROM PhoneTable WHERE tel='0120-999-999' OR email='phone@t.com';
取得したデータ
user | TEL | contract_date | |
---|---|---|---|
2 | 0120-999-999 | test@t.com | 2019年7月4日 |
2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
まとめ
Djangoにおける主要なデータベースからのデータ取得方法と、それに対応するSQL文についてまとめました。
今後はINSERTやUPDATE関連もまとめたいと思います。