DjangoのORMことはじめ
2018.01.27 読書会スペシャル 於 GEEKLAB.NAGANO
自己紹介
にしざわこういち
twitter: @koty
GEEKLAB.NAGANO 管理人見習い
SIer(Javaとか.NETとか)→現職(Python/Django/AWS/Vue.js)
Django概要
● Pythonで作られたフルスタック・フレームワーク
● ORM - Object Relational Mapping オブジェクト関係マッピング が優れている。
● 管理サイトが素晴らしい。
● 学習コストが低い
 引用元:Python Django入門 (1) - Qiita
これに加え「DB Migrationが便利」                     参考:普段Django
を使っている人間がruby on railsを勉強してみた話 - slideshare
DjangoのORMことはじめ
SQLアンチパターン 読書会スペシャルということで、
DjangoのORM周辺の話をします
目次
● Model
● Query
○ select
○ insert
○ update
○ delete
○ 外部キー先のデータの取得
● DB Migration
Model
from django.db import models
class Tag(models.Model):
url = models.CharField(max_length=100)
class User(models.Model):
username = models.CharField(max_length=100)
class Profile(models.Model):
tags = models.ManyToManyField(Tag, related_name='profiles')
user = models.ForeignKey(User, unique=True, related_name='profile')
多対多
1対1
primary_key=True
を指定しない場合は、
idという列ができる
select
単純な全件select
users = User.objects.all().order_by(‘username’)
# select id, username from user order by username;
単純な条件
users2 = User.objects.filter(username='koty')
# select id, username from user where username='koty';
select
ユニークキーでの取得
try:
koty = User.objects.get(id=1)
except User.DoesNotExist:
pass
except User.MultipleObjectsReturned:
pass
0件のときは例外が発生
2件以上のときも例外が発生
select
あいまい検索
users3 = User.objects.filter(username__startswith='koty')
# select id, username from user where username like 'koty%';
users4 = User.objects.filter(username__endswith='koty')
# select id, username from user where username like '%koty';
users5 = User.objects.filter(username__contains='koty')
# select id, username from user where username like '%koty%';
select
group by
from django.db.models import Count
Tag.objects.values('url') 
.annotate(url_count=Count('url')) 
.filter(url_count__gt=1)
# select url count(username) as url_count from tag group by url having
count(url)>1
insert
koty2 = User.objects.create(username='koty2')
# insert into user ('username') values ('koty2')
koty2 = User(username='koty2')
koty2.save()
# insert into user ('username') values ('koty2')
update と delete
koty2.username = 'koty3'
koty2.save()
# update user set username='koty3' where id=2
koty2.delete()
# delete from user where id=2;
外部キー先のデータの取得
p = Profile.objects.get(id=1)
u = p.user
profiles = Profile.objects.all()
for p in profiles:
print(p.user)
profiles = Profile.objects.select_related('user').all()
# select profile.id, profile.user_id, user.id, user.username
from user inner join user on profile.user_id=user.id
参照された時点で select が走る
N+1 問題
select_relatedでフィールドを指定すると
JOINになる
外部キー先のデータの取得
逆方向の参照
profiles = user.profile.all()
profile = profiles[0]
user = models.OneToOneField(User, related_name='profile')
profile = user.profile OneToOneフィールドで定義すれば単一オブジェクトを取得で
きる
外部キー先のデータの取得
逆方向参照でのN+1問題への対応
users = User.objects.prefetch_related('profile').all()
# select id, user_id from profile where id user_id in (1, 2);
prefetch_relatedで事前に取得しておける
ナマSQL
Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
ナマSQLも書けるが、得体の知れない負けた感
https://docs.djangoproject.com/en/2.0/topics/db/sql/
ログ出力
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
},
},
}
どんなSQLが発行されるか、普段の開発から注意を
払っておく
DB Migration
modelにフィールドを追加
class User(models.Model):
username = models.CharField(max_length=100)
birthday = models.DateField(null=True)
> ./manage.py makemigrations
> ./manage.py migrate
migrationファイルが生成される
migration適用。alter table が走る
←フィールドを追加
おしまい
Djangoはいいぞ

DjangoのORMことはじめ