Djangoのモデルやデータベース関連のメモが多くなったので、データベースが直接関わる箇所をメモから切り離してみた。
指定したカラムを読み込む、読み込まない、
読み込む場合
Hoge.objects.only('fuga','hage')
読み込まない場合
Hoge.objects.defer('fuga','hage')
でおk
save時のクエリを見る
from django.db import connection
Hoge().save()
print(connection.queries[-1])
参考:How can i get query for .save() in django?
リクエストパラメーターをモデルに入れる
# views.py
def a_view(request):
hoge = Fuga(request.GET) #新たにインスタンス作る場合
fuga = Fuga.objects.get(pk=1)
hage = Fuga(request.GET,instance=fuga) #既にあるインスタンスに入れる場合
instance=xxxとやるのを何度か忘れた><
DjangoでGroup By
DjangoでGroup Byして何個かのフィールドのデータで例えば、日付とその日に登録したデータの件数だけ欲しい時
# views.py
q = Hoge.objects.filter(date__gte = date).filter(date__lt = dto).extra({'date':"date_trunc('day',date)"})
q.query.group_by = ['date']
values = q.values('date').order_by().annotate(c = Count('id'))
こんな感じでいける。ちなみにPostgreSQL。dateが時間の情報も持っているのでPostgreSQLのdate_trunc関数を使ってdateを日付の情報だけにしてから、group_byする。
annotateは集計した値を新しいフィールド(ここだとc)に入れる。
例えばvalues[0].cでアクセスできる。このコードで、日付とその日に登録したデータ件数が入った辞書のリストを得ることが出来る
QuerySetで吐いたクエリを出力
# views.py
h = Hoge.objects.all()
print h.query
でおk。でもクエリが例外出す時には出せないような・・・
debug_toolbar使えば見れるか
参考:Djangoで、デバック用のSQLクエリの表示 一言多いプログラマーの独り言
MultiValueDictKeyError
モデルをgetする時にキーに何も値が指定されてなかったりすると起こる。例えば
instance = SampleModel.objects.get(id=key)
でkeyに何も設定されてないと起こるみたい。出た時に何回かイチイチぐぐったのでメモ
PostgreSQLでDateTimeFieldの値を日付に対してグルーピングしたい
PostgreSQLではタイムゾーン入りのタイムスタンプで登録されるので、
q = Hoge.objects.extra({"date":"date_trunc('day',date)"})
q.query.group_by['date']
みたいな構文で日付だけ取り出して、グルーピングしようとすると、PostgreSQLの設定でタイムゾーンがUTCになっていた場合に、2014/12/17 00:00:00+9のデータは2014/12/16 15:00:00+0と認識されるので12/16のデータとしてグルーピングされてしまう。日本のタイムゾーンでやってると、これはまずいので、以下のようなコードにすると良い
q = Hoge.objects.extra({"date":"date_trunc('day',date AT TIME ZONE 'JST')"})
q.query.group_by['date']
Many to manyなモデルで中間テーブルをthroughで指定、かつ自己参照な場合の設定
symmetrical設定をすると良いらしい
class Member(models.Model):
name = models.CharField(max_length=100)
friends = models.ManyToManyField('self', through = 'MemberFriends', symmetrical = False)
class MemberFriends(models.Model):
f = models.ForeignKey(Member, related_name = 'f')
t = models.ForeignKey(Member, related_name = 't')
comment = model.CharField(max_length = 100)
こんな感じ
dbにインデックスを付ける
インデックスの場合はフィールド定義にdb_index=Trueを付けるとおk
hoge = models.TextField(db_index=true)
複合インデックスの場合はMetaクラスでindex_togetherを定義する
class Hoge(models.Model):
....
class Meta:
index_together = [
['hoge','fuga']
]
この場合インデックス順はhoge->fugaとなる。
ForeignKeyで逆参照をしたくない場合
related_nameを+にするか末尾に+を付けるかで対応出来る。
user = models.ForeignKey(User, related_name='user+')
こんな感じ
生sqlを使う時にin句にパラメーターを使いたい
タプルで指定すれば良い。リストは試してない。
results = Hoge.objects.raw("select * from application_hoge where fuga in %s", tuple(fuga))
参考:passing lists or tuples as arguments in django raw sql - Stack Overflow
サブクエリの使い方
例えばMemberモデルにname (名前)やboss(上司の名前)、stage(肩書き)フィールドがあるとして、肩書きが部長の上司を持つMemberを取り出したい場合はこんな感じ
a_query = Member.objects.filter(stage='部長').value_list('name')
b_query = Member.objects.filter(boss__in = a_query)
select * from member where boss in (select name from member where stage='部長');
を実行したような感じになる。
参考:python - how to subquery in queryset in django? - Stack Overflow
モデル削除時にリレーション先を削除しない
リレーションを貼ったモデルを削除する時は、通常リレーション先のモデルも削除する。 その挙動を変えたい時は、ForeignKeyフィールドにon_deleteの項目を追加すると良い。 リレーション先を削除したくない場合は次のように書けば良い。
class Hoge(models.Model):
fuga = models.ForeignKey(null=True, on_delete=models.SET_NULL)
必ずnull=Trueが必要なことに注意。 他にも
- CASCADE
- 初期値
- PROTECT
- 削除しようとしたら例外を出す
- SET_DEFAULT
- 初期値を入れる(default=...が必要)
- SET
- 削除時に関数を呼び出す
がある。
参考:Model field reference | Django documentation | Django
リレーション先があるかどうかを調べるクエリ
Hoge.objects.filter(fuga__isnull=False)
みたいにする
参考:Filter Django objects where related object exists - Stack Overflow
DateTimeFieldの年の値だけdistinctで取得
djangoのモデルでは綺麗に実装する方法がある
Hoge.objects.dates('date_column', 'year')
でおk
参考:How do I get each distinct 'year' value out of a Django model with a datetime field?
DateTimeFieldの年についてfilter
2020年の日付を持つデータに絞り込みたい場合は
Hoge.objects.filter(date_column__year = 2020)
こんな感じ
参考:Django: Filtering datetime field by only the year value?
PostgreSQLでArrayField
PostgreSQLを使っている場合はdbに配列型のカラムを設定できる(ArrayField) ArrayFieldについてある要素を含むものを取り出す場合は、coitainsを用いる
Hoge.objects.filter(fugas__contains=['django'])