akiyoko blog

akiyoko の IT技術系ブログです

Django ORM の SQL を出力する方法まとめ

Django ORM を使っていると、どういった SQL が発行されているか、クエリの内容を出力したいときが多々あります。

SQL を出力する方法についてはいくつか方法がありますが、今回はその方法を思いつく限りピックアップしてみようと思います。

 

1)QuerySet の query を print する

個人的に一番よく使う方法。
実際に SQL を発行せずに、発行予定の SELECT文を出力することができます。

Django shell だろうが、pdb デバッグ中だろうが、PyCharm のデバッグ中だろうが、いつでも利用することができます。

例えば、Django shell で使う場合はこんな感じ。

$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> print User.objects.filter(pk=1).query
SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 1


事前に何も設定しなくても、デバッグ中に手っ取り早く発行された SQL が確認できるのが特徴です。しかしながら、発行された SQL をリアルタイムに確認することはできません。


(参考)How can I see the raw SQL queries Django is running? - Stack Overflow


 

2)DefaultConnectionProxy の queries を出力する

直前に発行された SQL を確認することができます。
1)とセットで使うことが多いでしょうか。

$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> User.objects.filter(pk=1)
[<User: user-1>]
>>> User.objects.filter(pk=2)
[<User: admin>]

>>> from django.db import connection
>>> connection.queries
[{u'time': u'0.000', u'sql': u'SET SQL_AUTO_IS_NULL = 0'}, {u'time': u'0.000', u'sql': u'SET SQL_AUTO_IS_NULL = 0'}, {u'time': u'0.000', u'sql': u'SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 1 LIMIT 21'}, {u'time': u'0.001', u'sql': u'SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 2 LIMIT 21'}]
>>> connection.queries[-1]
{u'time': u'0.001', u'sql': u'SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 2 LIMIT 21'}

発行したクエリは、'time'(実行時間)と 'sql' の dict として、発行された順に後ろに追加されていきます。慣れるまで、出力内容が少し読みにくいのが難点でしょうか。

またこちらの方法でも、発行された SQL をリアルタイムに確認することはできません。


(参考)How can I see the raw SQL queries Django is running? - Stack Overflow


 

3)django-debug-toolbar の SQL Panel を使う

プラグインのインストールが必要な物の、一番楽チンな方法。

Django を runserver で起動して、実際に画面を操作してから、右側に表示される SQL Panel を開いて確認するだけです。


django-debug-toolbar の SQL Panel を使うには、条件がいくつかあります。

  • DEBUG = True
  • 'django.contrib.staticfiles' が INSTALLED_APPS に設定済み *1

他にも、settings.py に以下の設定が必要です。 *2

INSTALLED_APPS += ('debug_toolbar',)

def always_show_toolbar(request):
    return True

DEBUG_TOOLBAR_CONFIG = {
    'SHOW_TOOLBAR_CALLBACK': '%s.always_show_toolbar' % __name__,
}


このように、画面からサクサクっと操作が可能です。楽チンですね。

f:id:akiyoko:20160804221039p:plain

f:id:akiyoko:20160804221058p:plain


インストール方法

$ pip install django-debug-toolbar

ちなみに、検証時の環境は以下の通りでした。

  • Python 2.7.6
  • Django (1.9.8)
  • django-debug-toolbar (1.5)


django-debug-toolbar のさらに詳しい説明については、以下の書籍が非常に有用です。「14-04 Django Debug Toolbar」の章に詳しい説明が載っています。
Django 以外にも、Python での開発手法についてのノウハウがいろいろ詰まっていてオススメです。

Pythonプロフェッショナルプログラミング第2版

Pythonプロフェッショナルプログラミング第2版



(参考)


 

4)django-debug-toolbar の debugsqlshell を使う

3)の django-debug-toolbar をインストールしているのであれば、通常の Django shell ではなく debugsqlshell を起動することで、発行される SQL を随時確認することができます。

$ python manage.py debugsqlshell
>>> from django.contrib.auth.models import User
>>> User.objects.filter()

SET SQL_AUTO_IS_NULL = 0 [0.80ms]
SELECT `auth_user`.`id`,
       `auth_user`.`password`,
       `auth_user`.`last_login`,
       `auth_user`.`is_superuser`,
       `auth_user`.`username`,
       `auth_user`.`first_name`,
       `auth_user`.`last_name`,
       `auth_user`.`email`,
       `auth_user`.`is_staff`,
       `auth_user`.`is_active`,
       `auth_user`.`date_joined`
FROM `auth_user` LIMIT 21 [0.19ms]
[<User: admin>]


 

5)django-extensions の shell_plus を --print-sql オプションで起動する

「django-extensions」は、「Django フレームワークの機能を便利に拡張する、管理コマンドやデータベースフィールドなどの詰め合わせ」 *3 です。

django-extensions の shell_plus を --print-sql オプションで起動すれば、4)と同じような機能を使うことができます。

$ python manage.py shell_plus --print-sql
>>> from django.contrib.auth.models import User
>>> User.objects.filter()

SET SQL_AUTO_IS_NULL = 0

Execution time: 0.000056s [Database: default]

SELECT `auth_user`.`id`,
       `auth_user`.`password`,
       `auth_user`.`last_login`,
       `auth_user`.`is_superuser`,
       `auth_user`.`username`,
       `auth_user`.`first_name`,
       `auth_user`.`last_name`,
       `auth_user`.`email`,
       `auth_user`.`is_staff`,
       `auth_user`.`is_active`,
       `auth_user`.`date_joined`
FROM `auth_user` LIMIT 21

Execution time: 0.000391s [Database: default]

[<User: admin>]

この shell_plus には autoloading という機能があるらしいのですが、正直なところ使ったことはありません。



インストール方法

$ pip install django-extensions

settings.py に「django_extensions」を追加。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_extensions',
]

(参考)


 

6)django.db.backends のログレベルを動的に変更

事前の準備がほとんど必要なく、1)や 2)のように明示的に SQL を出力する必要がないので、お手軽に使えます。

Django shell で使うケースが多いかもしれません。

$ python manage.py shell
>>>import logging
>>>l = logging.getLogger('django.db.backends')
>>>l.setLevel(logging.DEBUG)
>>>l.addHandler(logging.StreamHandler())

>>> from django.contrib.auth.models import User
>>> User.objects.filter(pk=1)
(0.000) SET SQL_AUTO_IS_NULL = 0; args=None
(0.000) SET SQL_AUTO_IS_NULL = 0; args=None
(0.000) SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 1 LIMIT 21; args=(1,)
[<User: user-1>]


 

7)settings.py の LOGGING を設定

ログに出力したり、コンソールに出力したりと、いろいろ柔軟に設定可能です。リアルタイムに発行される SQL が確認できるのも、この方法の特徴です。

なお、「DEBUG = True」でないと使えないので注意が必要です。


settings.py の設定例

DEBUG = True
LOGGING = {
    'disable_existing_loggers': False,
    'version': 1,
    'handlers': {
        'console': {
            # logging handler that outputs log messages to terminal
            'class': 'logging.StreamHandler',
            'level': 'DEBUG', # message level to be written to console
        },
    },
    'loggers': {
        '': {
            # this sets root level logger to log debug and higher level
            # logs to console. All other loggers inherit settings from
            # root level logger.
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False, # this tells logger to send logging message
                                # to its parent (will send if set to True)
        },
        'django.db': {
            # django also has database level logging
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}


ただしこの方法だと、ログやコンソールが大量の SQL ですぐに埋め尽くされてしまうので、1)や 2)の方法を使って、確認したい SQL だけをピンポイントに出力するようにした方が開発中は捗るかもしれません。


(参考)django - log all sql queries - Stack Overflow


 

まとめ

今回は、Django ORM の SQL を出力する方法として7種類のやり方を紹介しました。
この中から、目的や状況に応じてやり方を使い分けるようにすると、開発の効率もグンとアップすると思います。

良い Django ライフを!

*1:Django 1.9 では、デフォルトで INSTALLED_APPS に設定済みです。

*2:ミニマムな設定です。

*3:Pythonプロフェッショナルプログラミング第2版 より引用