【Django】発行されるクエリの確認方法(django-debug-toolbar)

クエリの情報の確認ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Django で開発したウェブアプリからデータベースに発行されるクエリの確認方法について紹介していきます!

クエリとはデータベースに対して発行される要求であり、このクエリを発行することで、データベースからレコードを取得したり、データベースにレコードを保存したりすることができます。

Django の魅力の1つはクエリを意識せずにウェブアプリが開発できるという点にあると思います。そのため、クエリの知識なしに、アイデア1つでウェブアプリを開発し、それを全世界に対して公開することができます。

ですが、クエリの意識が不要な分、パフォーマンスが劣化する可能性も高くなります。もしかしたらあなたが開発するウェブアプリでは大量にクエリが発行されるようになっており、その分、反応速度や処理速度が遅くなっている可能性もあります。

なので、クエリの意識が不要であると言えども、自身のウェブアプリがどのようにクエリを発行しているかは最低限知っておいた方が良いと思います。このクエリの情報がパフォーマンスの改善につながる可能性があります。

でも、どうやってウェブアプリが発行するクエリを確認すれば良いでしょうか?

この点について、このページでは解説を行なっていきます!

発行されるクエリを確認する方法

ウェブアプリからデータベースに対して発行されるクエリを確認するには、ツールやライブラリを利用するのが手っ取り早いです。

django-debug-toolbar

で、今回紹介するのが django-debug-toolbar と呼ばれるライブラリになります。このライブラリを利用すれば、あなたが開発するウェブアプリがデータベースに対して発行するクエリの内容を確認することができます。

ウェブアプリが発行するクエリが確認できる様子

また、クエリの内容だけでなく、クエリの発行タイミングを確認することもできます。要は、どの処理が実行された時にクエリが発行されるのかを確認することが可能です。もちろん、クエリが発行される回数も確認することもできます。

ウェブアプリがクエリを発行するタイミングが確認できる様子

こういった情報を確認することで、Django で開発したウェブアプリがクエリを発行するタイミングを知ることができますし、クエリの発行回数等に基づいてウェブアプリのパフォーマンスを向上させることもできます。例えば、意図せずクエリが大量に発行されていることが確認できた場合、それを修正することでウェブアプリのパフォーマンスを向上させることができます。

逆に、こういったクエリの情報が確認できないと、パフォーマンス向上の糸口を見つけることすら大変です。

さらに、クエリとは無関係になりますが、django-debug-toolbar ではウェブアプリが動作する際の Django フレームワークや MTV 等の各関数・各メソッドの呼び出し関係を確認することもできます。Django で開発したウェブアプリがどのように動作してページを表示しているのかを知りたい方には有益な情報になると思います。また、各関数や各メソッドの処理に要した時間も表示されるため、パフォーマンスの改善が必要な箇所も見つけやすくなります。

関数・メソッドの呼び出し関係が確認できる様子

スポンサーリンク

その他の方法(ログの詳細化)

その他にも、ウェブアプリが出力するログを詳細化してやることでも発行されるクエリの内容を確認することも可能です。

例えば、下記の LOGGING の設定を settings.py に追記してやればウェブアプリが動作する際にコンソールの出力されるログを増やすことができ、それによってデータベースに対して発行されるクエリも出力されるようになります。

ログの詳細かの設定
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'formatters': {
        'django.server': {
            '()': 'django.utils.log.ServerFormatter',
            'format': '[{server_time}] {message}',
            'style': '{',
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        },
        'django.server': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'django.server',
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'mail_admins'],
            'level': 'DEBUG',
        },
        'django.server': {
            'handlers': ['django.server'],
            'level': 'INFO',
            'propagate': False,
        },
    }
}

この方法では他のライブラリのインストールも不要で、settings.py に上記をコピペすれば良いだけなので手順としては楽です。ただ、他の情報のログも増えるため、ログが多くなりすぎてクエリの確認が大変になります。また、デバッガーでステップ実行すると、どうもクエリが余計に発行されるようになるようで、ステップ実行しながらログを見てクエリの発行タイミングを調べるのも少しやりにくいです。

ということで、多少手順は複雑になりますが、クエリの情報を確認するのであれば django-debug-toolbar を利用する方が良いと思います。

MEMO

ちなみに、django-debug-toolbar を利用するように設定した場合、上記のように LOGGING の設定を追加したとしてもクエリの内容がログに出力されなくなるようです

django-debug-toolbar の使い方

ということで、django-debug-toolbar を利用する方であれば、このツールは是非とも使いこなせるようになっておきたいところです。次は、この django-debug-toolbar の使い方について解説していきます。

django-debug-toolbar のインストール

では、まずは django-debug-toolbar のインストール方法について説明します。このインストールは、他の Python のモジュール・ライブラリ同様に pip コマンドで実現可能です。

環境によって pip コマンドの利用方法は異なりますが、基本的には下記のように python コマンドから pip モジュールを指定し、さらに引数に install django-debug-toolbar を指定してやれば django-debug-toolba をインストールすることができます。

% python -m pip install django-debug-toolbar

コマンド実行後に下記のようなメッセージが表示されれば、インストールに成功したことになります。

Successfully installed django-debug-toolbar-4.1.0

スポンサーリンク

settings.py の設定

django-debug-toolbar を利用するためには、Django のプロジェクトで django-debug-toolbar で利用するための設定が必要となります。この設定は、プロジェクトを作成すると自動的に生成される settings.py を変更することで行います。

MEMO

お察しの通り、settings.py は Django のプロジェクトごとに用意されることになりますので、異なるプロジェクトで django-debug-toolbar を利用する場合は毎回設定が必要となります

また、Django ではプロジェクトは下記コマンドによって作成することが可能です

% django-admin startproject プロジェクト名

django-debug-toolbar を利用するためには、settings.py で大きく分けて3つの設定を最低限行う必要があります。今回はプロジェクトを新規作成することを想定して解説を行なっていくため、基本的に settings.py はデフォルト状態のままであることを前提としています。この場合は、下記で示す3つの設定のみを行えば良いですが、既存のプロジェクトで settings.py を変更している場合、他の設定も必要になる可能性があるので注意してください。

INSTALLED_APPS の変更

1つ目の設定が INSTALLED_APPS のリストへの 'debug_toolbar' の追加となります。アプリを開発する際は毎回 INSTALLED_APPS の変更を行なっていると思いますが、それと同じ要領で 'debug_toolbar' を追記してやれば良いです。

INSTALLED_APPSの変更
INSTALLED_APPS = [
    # 略
    'django.contrib.staticfiles',
    'debug_toolbar', #追記
]

MIDDLEWARE の変更

続いて、MIDDLEWARE のリストに 'debug_toolbar.middleware.DebugToolbarMiddleware' を追記します。この追記箇所は重要らしく、基本的には MIDDLEWARE のリストの先頭に追記するようにしてください。ただし、レスポンスのコンテンツをエンコードするようなミドルウェアを利用している場合、そのミドルウェアよりも後ろ側に追記を行う必要があります。MIDDLEWARE のリストを変更していない場合は、リストの先頭に追記を行うので良いです。

MIDDLEWAREの変更
MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware', #追記
    'django.middleware.security.SecurityMiddleware',
    # 略
]

INTERNAL_IPS の追加

settings.py の変更の最後として INTERNAL_IPS への IP アドレス '127.0.0.1' の追加を行います。settings.py のデフォルト状態では INTERNAL_IPS の設定は記述されていないため、その場合は下記を丸ごと settings.py にコピペしてやれば良いです。コピペ先は settings.py のどこでも良いですが、迷ったら末尾にコピペするので良いです。

INTERNAL_IPSの追加
INTERNAL_IPS = [
    '127.0.0.1',
]

urls.py の設定

また、settings.py だけでなく、urls.py の設定も必要となります。この urls.py はアプリ側のものではなくプロジェクトの側のものであるので注意してください。settings.py と同じフォルダにある urls.py を変更すると考えてもらえばわかりやすいと思います。

urls.pyの変更
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('__debug__/', include('debug_toolbar.urls')),
]

アプリの準備

以上で、プロジェクトから django-debug-toolbar を利用する準備は整ったことになります。

ただ、django-debug-toolbar はアプリのページを表示した際に動作するツールとなるため、最低限、アプリのページを表示できるようにアプリの準備を行う必要があります。

このアプリの手順はいつも通りの手順で行えば良いですが、django-debug-toolbar で発行されたクエリの情報を確認するためには、レスポンスのボディに body タグが必ず必要であることに注意してください。もちろん、クエリの情報を確認するのですから、アプリからデータベースを操作するような処理も必要となります。

前述の通り、アプリ側はいつも通りの手順で準備すれば良いですが、念の為、ここからアプリ側の準備手順についてもサラッと説明をしていきたいと思います。不要という方は、django-debug-toolbar での情報確認 までスキップしていただければと思います。

アプリの作成

まずはアプリを startapp コマンドで作成します。プロジェクトのフォルダ内(manage.py が存在するフォルダ内)で下記コマンドを実行してアプリを作成します。ここではアプリ名は appli としています。

% python manage.py startapp appli

アプリの登録

続いて、プロジェクトへのアプリの登録を行います。先ほども変更を行なった settings.py を開き、INSTALLED_APPS'appli' を追記してください。

settings.py(アプリの登録)
INSTALLED_APPS = [
    'appli', #追記
    'django.contrib.admin',
]

モデルの定義

次はモデルの定義を行います。今回はデータベースへ発行されるクエリを確認するため、このモデルの定義は必須です。ですが、定義するモデルはなんでも良いです。今回は、例として、アプリフォルダ内の models.py を下記のように変更し、UserComment を定義するものとしたいと思います。

models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=256)

class Comment(models.Model):
    text = models.CharField(max_length=256)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

ビューの定義

次はビューの定義を行なっていきます。アプリフォルダ内の views.py を開き、下記のように変更してください。

views.py
from django.shortcuts import render
from django.http.response import HttpResponse
from .models import Comment, User

def create(request):

    for i in range(10):
        # Userを作成
        user = User(name='Yamada_' + str(i))
        user.save()

        # Commentを作成
        comment = Comment(text='Hello')
        comment.user = user
        comment.save()

    return HttpResponse('<body>作成完了</body>')

def index(request):

    comments = Comment.objects.all()
    
    context = {
        'comments' : comments
    }

    return render(request, 'appli/index.html', context)

少し補足しておくと、上記における create 関数は models.py に定義した CommentUser のインスタンスを 10 個ずつ作成する関数になっており、index 関数は、Comment の全インスタンス(全レコード)の情報を表示する関数になっています。

index 関数に関しては render を利用せず、直接 HttpResponse を返却するようにしていますが、レスポンスのボディには body タグが存在するため、これだけでも django-debug-toolbar を利用したクエリの確認等は行うことができます。

また、create 関数と index 関数では両方ともデータベースへの操作が行われているため、そのためのクエリが発行されるはずです。django-debug-toolbar での情報確認 では、このクエリの内容を確認していきます。

テンプレートの定義

index 関数からはテンプレートファイル index.html を利用するようになっているため、次に、この index.html を用意していきます。

まず、appli フォルダの下に templates フォルダを作成し、さらに templates フォルダの中に appli フォルダを作成します。そして、最後に作成した appli フォルダの下に index.html を新規作成し、下記のように中身を変更して保存します。

index.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
</head>
<body>
    <main>
        <table>
            <thead>
                <tr><th>コメント</th><th>投稿者</th></tr>
            </thead>
            <tbody>
                {% for comment in comments %}
                <tr>
                    <td>{{ comment.text }}</td>
                    <td>{{ comment.user.name }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </main>
</body>
</html>

urls.py の作成(アプリ)

最後に urls.py を変更して URL とビューのマッピングを行います。まず、appli フォルダの下に urls.py を新規作成し、この urls.py の中身を下記のように変更して保存します。

urls.py(アプリ)
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
]

urls.py の作成(プロジェクト)

次に、プロジェクト側の urls.py下記のように変更して保存します。

urls.py(プロジェクト)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('appli/', include('appli.urls')), #追記
    path('__debug__/', include('debug_toolbar.urls')),
]

マイグレーションの実行

以上でファイルの変更は完了です。

最後にマイグレーションを実行して models.py で定義したモデルに対応するテーブルをデータベースに作成します。

マイグレーションはプロジェクトのフォルダの下(manage.py が存在するフォルダ内)で下記の2つのコマンドを実行することで実現できます。

% python manage.py makemigrations
% python manage.py migrate

スポンサーリンク

django-debug-toolbar での情報確認

以上で、django-debug-toolbar を利用するための準備が完了したことになります。

クエリの内容を確認する

ということで、次は django-debug-toolbar を利用してクエリの内容等を確認していきましょう!

いつも通りの手順でページを表示する

django-debug-toolbar を利用するように設定しておけば、いつも通りにページを表示した際に、ページの右側にツールバーが表示されるようになり、そのツールバーからクエリの内容等を確認することができます。

なので、まずはいつも通りページの表示を行なっていきましょう。ページを表示するためには、Django 開発用ウェブサーバーを起動しておく必要があるため、プロジェクトフォルダの下(manage.py が存在するフォルダ内)で下記コマンドを実行してサーバーを起動します。

% python manage.py runserver

サーバーが起動すれば、次はウェブブラウザを起動して下記の URL を開きます。下記の URL を開くと create 関数が実行され、models.py で定義した CommentUser のインスタンスが 10 個ずつ作成され、データベースに保存されることになります。

http://localhost:8000/appli/create/

ツールバーからクエリの情報を表示する

といっても、この URL を開いてもページに表示されるのは 作成完了 というメッセージのみとなります。ですが、django-debug-toolbar を利用するように設定しているため、ページの右側に見慣れない黒いツールバーが表示されていることが確認できるはずです。このツールバーから、ページ表示に関する様々な情報を確認することができます。

ページにツールバーが表示される様子

まずはクエリの内容を確認してみましょう!このクエリの内容はツールバーの SQL をクリックすることで確認することができます。

クエリの内容の確認手順

SQL をクリックすれば、下の図のようなページが表示されるはずです。これがまさに、ページ表示時に発行されたクエリの一覧となります。

発行されたクエリ一覧

前述の通り、create 関数では CommentUser のインスタンスが 10 個ずつ作成されるため合計 20 回のクエリが発行されることになります。その発行された 20 回分の全てのクエリの内容が、上図のページから確認することが可能となっています。また、タイムラインも表示されているため、どのクエリの処理にどれくらい時間がかかっているのかを確認することもできます。

クエリの内容を確認する

さらに、各クエリの内容は各行から確認することが可能です。例えば下の図のオレンジ枠内を確認すれば、

クエリの内容

発行されたクエリが下記であることを確認することができます。下記では appli_user というテーブルに name フィールドが Yamada_0 であるレコードを追加するクエリが発行されているようですね。

INSERT INTO "appli_user" ("name") SELECT '''Yamada_0''' RETURNING "appli_user"."id"

クエリの発行タイミングを確認する

また、+ ボタンをクリックすれば、そのクエリが発行されたタイミングを確認することもできます。下の図であれば、views.pycreate 関数における user.save() が実行されたタイミングでクエリが発行されていることを確認することができます。

クエリを発行する処理の確認

こんな感じで、django-debug-toolbar を利用すれば、発行されたクエリの内容やクエリの発行タイミングを確認することが可能です。

クエリの発行で取得されたレコードを確認する

次は、下記 URL をウェブブラウザで開いてみましょう。この場合は views.py における index 関数が実行され、データベースに保存されている Comment の全インスタンスが取得され、そのインスタンスの text フィールドおよび、そのインスタンスと関連づけされた User のインスタンスの name フィールドが表示されることになります。データベースへの保存は、先ほどページを開く際に実行された create 関数ですでに行われているはずです。

http://localhost:8000/appli/

URL を開くと下の図のように、ページの描画結果に追加して、先ほどと同様にツールバーが表示されることが確認できると思います。

index実行結果とともにツールバーが表示される様子

そして、先ほどと同様に SQL をクリックすれば、ページを表示する際に発行されたクエリの内容を確認することが可能です。ただし、先ほどとは少しページの構成が異なっています。今回はデータベースに保存するのではなく、データベースから取得するためのクエリが発行されるため、このページからクエリの発行によって取得されたレコード(インスタンス)の情報が確認できるようになっています。

適当なクエリの行の Sel ボタンをクリックすれば、

取得されたレコードの確認方法1

下の図のように、そのクエリの発行によって取得されたレコード一覧が表示されることになります。

取得されたレコードの確認

テンプレート上でのクエリの発行タイミングも確認可能

また、+ ボタンをクリックすれば、そのクエリが発行されたタイミングを確認することができます。index 関数の場合、create 関数と違ってテンプレートファイルを利用していますが、この場合はテンプレートファイルのどこでクエリが発行されているのかを確認することが可能です。

テンプレートファイルにおけるクエリの発行タイミングの確認

このように、django-debug-toolbar を利用すれば、ページを表示する際に発行されたクエリの内容や発行タイミングを確認することが可能です。

クエリの情報を利用してパフォーマンスを改善する

ただ、これらの情報をボーっと眺めていても意味はなく、これらの情報から、ウェブアプリのパフォーマンス改善ポイントを調べていくことが重要となります。

例えばですが、http://localhost:8000/appli/ を開いた際のクエリの情報には、下の図で示すようなオレンジ色で示した 10 similar queries というメッセージが表示されていることを確認できると思います。

同様のクエリが何回も発行されてしまっていることを示すメッセージ

これはメッセージの通り、同じようなクエリが何回も発行されていることを示すメッセージであり、このクエリの発行に関しては改善可能かもしれないということを示唆しています。

このメッセージが表示されているクエリの発行タイミングを先ほどと同様の手順で調べると、発行タイミングは全てテンプレートファイル内における for ループの中の処理実行時であり、for ループの中で毎回インスタンスを取得するためのクエリが発行されていることを確認することができます。であれば、for ループ外で一度に必要なインスタンスを全てごっそり取得しておけば、for ループ内でのクエリの発行が不要となり、ウェブアプリのパフォーマンスが向上する可能性があります。

同様のクエリが何回も発行されているタイミング

データベースに詳しい人であればもう察していらっしゃるかもしれませんが、今回作成した index 関数および index.htmlN + 1 問題が発生する作りとなっており、実は views.py における index 関数の下記部分を、

N+1問題の発生原因
comments = Comment.objects.all()

下記のように変更すれば、先ほど示した for ループ内でのクエリの発行を 0 にすることができます。

N+1問題の発生原因
comments = Comment.objects.select_related('user').all()

実際にクエリの発行内容を確認すれば、下の図のようにクエリの発行回数が 1 回のみになっていることが確認できると思います。

クエリの発行回数が削減されている様子

このように、クエリの内容や発行タイミングを確認することで、自身のウェブアプリのパフォーマンスを低下させる原因となるクエリの発行を見つけ出すことができ、それを解決することでウェブアプリのパフォーマンス(反応速度など)を向上させることが可能となります。

Django を利用しているとクエリの発行に関してはあまり意識しないかもしれないですが、django-debug-toolbar を利用することで簡単にクエリの内容やクエリの発行タイミングを確認することができ、自身のウェブアプリがデータベースとどのようなやりとりをしているかを知ることができます。それを見るだけでも面白いのですが、パフォーマンス低下の原因につながるクエリが見つけ、それを改善していくことも重要となります。

その他の情報を確認する

ここまでクエリの情報の確認を行ってきましたが、みなさんお気づきの通り、ツールバーにはクエリの情報を確認する SQL だけでなく様々な項目があります。これらの項目をクリックすることで、いろんな情報を確認することが可能となっています。

ツールバーに他の項目があることを示す図

Settings の内容を確認する

ツールバーをクリックして情報を表示してみれば、大体どんな情報が確認可能かは理解していただけるのではないかと思います。例えば Settings をクリックすれば、プロジェクトの設定内容を全て確認することが可能となっています。

Settingsの表示結果

ここで表示されるのはプロジェクトの settings.py で設定した項目だけでなく、プロジェクトの settings.py では設定していない項目、つまりデフォルト値が指定されている項目についても確認することができます。

したがって、プロジェクトの settings.py で設定した内容以外の設定も確認することができますし、これらは settings.py で設定を上書きすることが可能な設定項目となっていますので、settings.py で設定可能な項目も網羅的に知ることができます。

ちなみに、settings.py で設定可能な項目については下記ページでも説明していますので、興味があればこちらも読んでみていただければと思います。

Djangoのsettings.pyで設定可能な項目とデフォルト値の調べ方の解説ページアイキャッチ 【Django】settings.py での設定可能項目やデフォルト値の調べ方

Profiling の内容を確認する

また、ツールバーの一番下には Profiling という項目があります。この Profiling は、おそらくデフォルトでは無効化されていますが、チェックボックスを ON にすることで有効化することができます。

Profilingの項目を示す図

この Profiling を有効化した後にページ表示を行い、この Profiling をクリックすれば、ページ表示時に実行された関数やメソッド、さらには各関数やメソッドで要した処理時間を確認することが可能です。

Profilingをクリックすると表示される情報

黄色背景が、Django でウェブアプリを開発する際に、主に開発者が実装を行う部分となるプロジェクトのファイルの関数やメソッドを表しています。

この情報から、自身のウェブアプリが動作する際に各関数やメソッドの処理に要した時間を確認することができ、どの処理がボトルネックになっているか知ることができます。もちろんこの情報は、ウェブアプリのパフォーマンス改善に活かすことができます。

また、パフォーマンス改善に活かすだけでなく、ウェブアプリがリクエストを受け取った際に、Django フレームワークや自身が開発するプロジェクト内の関数やメソッドが「どういう順序で実行されているか」を確認することも可能です。特に Django フレームワーク自体の処理の流れを知りたい人や、プロジェクト内の関数やメソッドが Django フレームワークからどのように利用されているのかを知りたい方にとっては有益な情報になると思います。

スポンサーリンク

まとめ

このページでは、Django で開発したウェブアプリが発行するクエリの情報を確認する方法について説明しました!

このクエリの情報は django-debug-toolbar を利用することで簡単に確認することが可能で、発行されるクエリの内容やクエリの発行タイミングを確認することができます。

例えば、ウェブアプリが標準出力に出力するログを増やすことでもクエリの情報を確認することは可能ではあるのですが、django-debug-toolbar が一番クエリの情報を確認しやすいと私は思ったので、この方法でのクエリの情報の確認について説明させていただきました。

普段は気にしないクエリかもしれないですが、django-debug-toolbar を使ってみるといろんな気づきが得られると思います。是非 django-debug-toolbar を利用して自身のウェブアプリがデータベースに発行しているクエリを確認してみてください!

Django 等のウェブアプリフレームワークを利用すればクエリやデータベースの知識がほとんど入らずにウェブアプリが開発できるので、今後クエリやデータベースの知識を持つ人が減っていく可能性があります。そんな中で、クエリやデータベースの知識・技術に長けているということは技術者としての強みになると思います。クエリや SQL、データベースに興味を持った方は是非、データベース関連の勉強もしてみてください!

おすすめ書籍(PR)

特にデータベース初心者の方には下記の スッキリわかるSQL入門 第3版 ドリル256問付き! がオススメです。題名の通りスッキリ理解できる内容となっていますし、所々にクイズ(問題)が散りばめられていますので、自身の内容の理解度を確認しながら勉強を進めることもできます。django-debug-toolbar で確認できるクエリの内容の意味も理解できるようになると思います!

同じカテゴリのページ一覧を表示