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'))


こんな感じでいける。ちなみにPostgreSQLdateが時間の情報も持っているので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で指定、かつ自己参照な場合の設定

参考:How do I write a Django model with ManyToMany relationsship with self through a Model - Stack Overflow

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