Djangoでのデータベースの扱い方②
どうも、夏映画を見に行きたいhashiです。
前回に続きDjango(ジャンゴ)というPythonのWebアプリケーションフレームワークでのデータベース操作についてまとめます。
今回は主にデータの挿入、更新とバリデーションチェックを扱います。
動作環境
テーブルの生成
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)
UserTable(表構造)
列 | 型 | NULLを許容する | 制約 |
---|---|---|---|
user_id | integer | NOT NULL | PRIMARY KEY |
name | varchar(30) | NOT NULL | |
age | integer |
PhoneTable(表構造)
列 | 型 | NULLを許容する | 制約 |
---|---|---|---|
id | integer | NOT NULL | PRIMARY KEY |
user_id | integer | NOT NULL | REFERENCES |
tel | varchar(15) | NOT NULL | |
varchar(254) | NOT NULL | ||
contract_date | date |
このように、ユーザー情報と携帯電話の情報という2つのテーブルが作られます。
定義した覚えのないid
という列がPhoneTableにありますが、これは primary_key=True
を指定していないテーブルに自動的に追加される自動連番のプライマリーキーです。
これによって各テーブルにプライマリーキーが必ず1つだけ存在するようになっています。
テーブルへのデータの挿入、更新
あらかじめテーブルにいくつかデータを入れておきます。これからの操作はこのデータを用いて行います。
UserTable
user_id | name | age |
---|---|---|
1 | tanaka | 34 |
2 | sato | |
3 | suzuki | 40 |
4 | sasaki | 24 |
PhoneTable
id | user_id | tel | contract_date | |
---|---|---|---|---|
1 | 1 | 03-1234-9876 | tanaka@t.com | 2018年10月1日 |
2 | 1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
3 | 2 | 0120-999-999 | test@t.com | 2019年7月4日 |
4 | 3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
5 | 2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
6 | 1 | 0120-976-543 | testtest@t.com | |
7 | 1 | 03-1234-9876 | karikari@test.com | |
8 | 4 | 111 | poi@poi.jp |
データの挿入
以下のような疑似SQL文を実行してデータを挿入したいとします。
INSERT INTO PhoneTable(user_id, tel, email, contract_date) VALUES (1, '0123-45-6789', 'insert@insert.jp', '2020-01-01');
ユーザーIDが1のユーザーの電話機の情報を新規に登録する際、Djangoでは以下のように記述します。
phone = PhoneTable() # オブジェクトの作成 phone.user = UserTable.objects.get(user_id=1) # 紐づくユーザー phone.tel = '0123-45-6789' # 電話番号 phone.email = 'insert@insert.jp' # メールアドレス phone.contract_date = '2020-01-01' # 契約日 phone.save() # データベースに反映させる
PhoneTableクラスのオブジェクトを作成、各フィールドに値を入れた後にsave()
メソッドを呼ぶことで値をデータベースに挿入することができます。
また、
phone.user = UserTable.objects.get(user_id=1)
は、以下のように書き換えることもできます。
phone.user_id = 1 # (外部キーのフィールド名)_id=(参照先列の値)
挿入後のデータ
id | user_id | tel | contract_date | |
---|---|---|---|---|
1 | 1 | 03-1234-9876 | tanaka@t.com | 2018年10月1日 |
2 | 1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
3 | 2 | 0120-999-999 | test@t.com | 2019年7月4日 |
4 | 3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
5 | 2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
6 | 1 | 0120-976-543 | testtest@t.com | |
7 | 1 | 03-1234-9876 | karikari@test.com | |
8 | 4 | 111 | poi@poi.jp | |
9 | 1 | 0123-45-6789 | insert@insert.jp | 2020年1月1日 |
このように、末尾にデータが挿入されました。
データの更新
以下のような疑似SQL文を実行して、データを更新したいとします。
UPDATE PhoneTable SET user_id = 2, tel = '000-000-0000', email= 'aa@a.aaa', contract_date = '2019-07-07' WHERE id = 9;
id=9
の電話機の情報を更新する際、Djangoでは以下のように記述します。
phone = PhoneTable() # オブジェクトの作成 phone.id = 9 # 主キーの指定 phone.user = UserTable.objects.get(user_id=2) # 紐づくユーザー phone.tel = '000-000-0000' # 電話番号 phone.email = 'aa@a.aaa' # メールアドレス phone.contract_date = '2019-07-07' # 契約日 phone.save() # データベースに反映させる
もしくは
phone = PhoneTable.object.get(id=9) # オブジェクトの取得 phone.user = UserTable.objects.get(user_id=2) # 紐づくユーザー phone.tel = '000-000-0000' # 電話番号 phone.email = 'aa@a.aaa' # メールアドレス phone.contract_date = '2019-07-07' # 契約日 phone.save() # データベースに反映させる
なんと前者ではデータの挿入時と記述方法が同じになっています。
この時どうやってDjangoがINSERTとUPDATEを見分けるかはドキュメントによると、
とあり、主キーの値を指定することに加えて、その値を持つ行がデータベース内に既に存在する場合、UPDATEが実行されるようです。
上に示したコードでは主キーであるidを指定しており、id=9
のデータがすでに存在するためUPDATEが実行されることになります。
更新後のデータ
id | user_id | tel | contract_date | |
---|---|---|---|---|
1 | 1 | 03-1234-9876 | tanaka@t.com | 2018年10月1日 |
2 | 1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
3 | 2 | 0120-999-999 | test@t.com | 2019年7月4日 |
4 | 3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
5 | 2 | 0120-68-1147 | phone@t.com | 2019年7月23日 |
6 | 1 | 0120-976-543 | testtest@t.com | |
7 | 1 | 03-1234-9876 | karikari@test.com | |
8 | 4 | 111 | poi@poi.jp | |
9 | 2 | 000-000-0000 | aa@a.aaa | 2019年7月7日 |
データのバリデーションチェック
入力データが必ずしも表構造にあっているとは限らないため、入力の確認をする必要があります。
そこで使われるのがfull_clean()
メソッドです。
もしこのメソッドを呼び出したオブジェクトに不正な値(型の違い、一意制約違反など…)があれば 例外(ValidationError)を投げて教えてくれます。
try: user.full_clean() except ValidationError as e:# 入力に不正な値があるとき # エラーの出たフィールドをユーザーに教えるなどの手続きをここに書く else: # 入力に不正な値がないとき user.save() # 安心してデータベースに反映することができる
ほかにもバリデーションチェックを行う方法はありますが省略します。
データの削除
以下のような疑似SQL文を実行してデータを削除したいとします。
DELETE PhoneTable WHERE user_id = 2;
user_id = 2
の電話機の情報を削除する際、Djangoでは以下のように記述します。
phone = PhoneTable.objects.filter(user_id=2) # 削除対象のオブジェクトの取得 phone.delete() # データベースから削除する
filter()
メソッドを使用して得られたクエリセットに delete()
メソッドを呼ぶことで一括削除を行えます。
削除後のデータ
id | user_id | tel | contract_date | |
---|---|---|---|---|
1 | 1 | 03-1234-9876 | tanaka@t.com | 2018年10月1日 |
2 | 1 | 090-1234-5678 | tanaka2@t.com | 2019年8月1日 |
4 | 3 | 0120-202-102 | temp@temp.com | 2018年11月7日 |
6 | 1 | 0120-976-543 | testtest@t.com | |
7 | 1 | 03-1234-9876 | karikari@test.com | |
8 | 4 | 111 | poi@poi.jp |
まとめ
Djangoにおけるデータベースへのデータ挿入、更新と削除方法についてまとめました。
前回の記事と合わせて、これで一通りCRUD操作が扱えるようになりました。