DjangoのORマッパーでSQL文を簡単に出力するサンプル

ForeignKeyとfilterのメモ - 憧れ駆動開発

この記事でDjangoのORMapperをちょっと試そうとしてうまくいかなかったって話があったけど、簡単にDjangoが出力するSQL文を見たいときはどうしたらいいのかな?

# -*- coding:utf-8 -*-
from django.db import models


class Bread(models.Model):
    class Meta:
        db_table = "bread"
        app_label = "sample"

    name = models.CharField(max_length=255)
    price = models.IntegerField()


class BreadStore(models.Model):
    class Meta:
        db_table = "bread_store"
        app_label = "sample"

    bread = models.ForeignKey(Bread)
    name = models.CharField(max_length=255)


def main():
    print BreadStore.objects.filter(bread__name="foo").query


if __name__ == "__main__":
    main()

実行結果

$ PYTHONPATH=.:foo DJANGO_SETTINGS_MODULE=foo.settings python foo/sample.py 
SELECT `bread_store`.`id`, `bread_store`.`bread_id`, `bread_store`.`name` FROM `bread_store` INNER JOIN `bread` ON (`bread_store`.`bread_id` = `bread`.`id`) WHERE `bread`.`name` = foo

QuerySet.queryからSQL文を見られる

上のコードの通り、QuerySet.queryをprintするとSQL文が見られる。
DjangoのORMapperを使ったときにどういうクエリが発行されるのか簡単に調べたいときは重宝する。

ちょっと試しのときはMetaのapp_labelを忘れないこと

上のコードのMetaって内部クラスを見るとapp_labelって属性を定義している。
これがないとこういうエラーになって動かない。

Traceback (most recent call last):
File "foo/sample.py", line 5, in
class Bread(models.Model):
File "/home/psappho/.virtualenvs/django/local/lib/python2.7/site-packages/django/db/models/base.py", line 54, in __new__
kwargs = {"app_label": model_module.__name__.split('.')[-2]}
IndexError: list index out of range

app_labelを書いておけば大丈夫。

models.pyをパッケージにしてモデル定義を分割したいときもMeta.app_labelが使える

Djangoのモデル定義は基本的にDjangoアプリのパッケージのmodels.pyに書いておかないとsyncdbの対象とかにならない。
何でなのかわかんないけど、"models"ってモジュールに書いてあることをわざわざ見ている。

だからmodels.pyに沢山モデルのコードを書いてしまって長くなった場合に、いくつかのモデル定義ごとにファイルを分割したくなるけど、何もしないとsyncdbの対象とかにならない。

例えば

app
└foo
   └models
      └spam
      └ham
      └egg

こういう風にmodelsをパッケージにしてspam, ham, eggにモデル定義を分割したい場合はMeta.app_labelを付けておくこと。

[app/foo/models/spam.py]

from django.db import models

class Spam(models.Model):
    class Meta:
        db_table = "spam"
        app_label = "foo"
    name = models.CharField(max_length=255)

[app/foo/models/__init__.py]

from app.foo.models.spam import Spam

追記: django.db.connection.queriesを見る方法について

Djangoの発行する生SQLが見たい - PYTHONIC BOOM BOOM HEAD

ボクの記事を見てconnection.queriesを見る方法もあるよって教えてくれた人がいました。

ただ、この方法はボクもこれは知っていたんですけど、これは本当にデータベースに流したSQLを後から確認する方法なので、対象テーブルが存在しないと実行できません。

あと、あんまり気にしなくていいけど、settings.DEBUG=Trueじゃないと見られません。

pdbとか対話型インタプリタでQuerySetに特定の操作をするとどういうクエリを出力するのか確認したいときは、QuerySet.queryをプリントするのが手軽かなーと思います。

この記事を書いていて気づいたんですけど、app_labelをつけるとかいう話は重要ではなかったですね(汗)
一番推したかったことはQuerySet.queryをプリントするってところでした。

レスをくれたaltnightさん、kk6さんに感謝です。