このページでは、Django で開発したウェブアプリからデータベースに発行されるクエリの確認方法について紹介していきます!
クエリとはデータベースに対して発行される要求であり、このクエリを発行することで、データベースからレコードを取得したり、データベースにレコードを保存したりすることができます。
Django の魅力の1つはクエリを意識せずにウェブアプリが開発できるという点にあると思います。そのため、クエリの知識なしに、アイデア1つでウェブアプリを開発し、それを全世界に対して公開することができます。
ですが、クエリの意識が不要な分、パフォーマンスが劣化する可能性も高くなります。もしかしたらあなたが開発するウェブアプリでは大量にクエリが発行されるようになっており、その分、反応速度や処理速度が遅くなっている可能性もあります。
なので、クエリの意識が不要であると言えども、自身のウェブアプリがどのようにクエリを発行しているかは最低限知っておいた方が良いと思います。このクエリの情報がパフォーマンスの改善につながる可能性があります。
でも、どうやってウェブアプリが発行するクエリを確認すれば良いでしょうか?
この点について、このページでは解説を行なっていきます!
Contents
発行されるクエリを確認する方法
ウェブアプリからデータベースに対して発行されるクエリを確認するには、ツールやライブラリを利用するのが手っ取り早いです。
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
を利用する方が良いと思います。
ちなみに、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
を変更することで行います。
お察しの通り、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 = [
# 略
'django.contrib.staticfiles',
'debug_toolbar', #追記
]
MIDDLEWARE
の変更
続いて、MIDDLEWARE
のリストに 'debug_toolbar.middleware.DebugToolbarMiddleware'
を追記します。この追記箇所は重要らしく、基本的には 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 = [
'127.0.0.1',
]
urls.py
の設定
また、settings.py
だけでなく、urls.py
の設定も必要となります。この urls.py
はアプリ側のものではなくプロジェクトの側のものであるので注意してください。settings.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'
を追記してください。
INSTALLED_APPS = [
'appli', #追記
'django.contrib.admin',
]
モデルの定義
次はモデルの定義を行います。今回はデータベースへ発行されるクエリを確認するため、このモデルの定義は必須です。ですが、定義するモデルはなんでも良いです。今回は、例として、アプリフォルダ内の models.py
を下記のように変更し、User
と Comment
を定義するものとしたいと思います。
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
を開き、下記のように変更してください。
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
に定義した Comment
と User
のインスタンスを 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
を新規作成し、下記のように中身を変更して保存します。
<!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
の中身を下記のように変更して保存します。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
path('create/', views.create),
]
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
で定義した Comment
と User
のインスタンスが 10
個ずつ作成され、データベースに保存されることになります。
http://localhost:8000/appli/create/
ツールバーからクエリの情報を表示する
といっても、この URL を開いてもページに表示されるのは 作成完了
というメッセージのみとなります。ですが、django-debug-toolbar
を利用するように設定しているため、ページの右側に見慣れない黒いツールバーが表示されていることが確認できるはずです。このツールバーから、ページ表示に関する様々な情報を確認することができます。
まずはクエリの内容を確認してみましょう!このクエリの内容はツールバーの SQL
をクリックすることで確認することができます。
SQL
をクリックすれば、下の図のようなページが表示されるはずです。これがまさに、ページ表示時に発行されたクエリの一覧となります。
前述の通り、create
関数では Comment
と User
のインスタンスが 10
個ずつ作成されるため合計 20
回のクエリが発行されることになります。その発行された 20
回分の全てのクエリの内容が、上図のページから確認することが可能となっています。また、タイムラインも表示されているため、どのクエリの処理にどれくらい時間がかかっているのかを確認することもできます。
クエリの内容を確認する
さらに、各クエリの内容は各行から確認することが可能です。例えば下の図のオレンジ枠内を確認すれば、
発行されたクエリが下記であることを確認することができます。下記では appli_user
というテーブルに name
フィールドが Yamada_0
であるレコードを追加するクエリが発行されているようですね。
INSERT INTO "appli_user" ("name") SELECT '''Yamada_0''' RETURNING "appli_user"."id"
クエリの発行タイミングを確認する
また、+
ボタンをクリックすれば、そのクエリが発行されたタイミングを確認することもできます。下の図であれば、views.py
の create
関数における user.save()
が実行されたタイミングでクエリが発行されていることを確認することができます。
こんな感じで、django-debug-toolbar
を利用すれば、発行されたクエリの内容やクエリの発行タイミングを確認することが可能です。
クエリの発行で取得されたレコードを確認する
次は、下記 URL をウェブブラウザで開いてみましょう。この場合は views.py
における index
関数が実行され、データベースに保存されている Comment
の全インスタンスが取得され、そのインスタンスの text
フィールドおよび、そのインスタンスと関連づけされた User
のインスタンスの name
フィールドが表示されることになります。データベースへの保存は、先ほどページを開く際に実行された create
関数ですでに行われているはずです。
http://localhost:8000/appli/
URL を開くと下の図のように、ページの描画結果に追加して、先ほどと同様にツールバーが表示されることが確認できると思います。
そして、先ほどと同様に SQL
をクリックすれば、ページを表示する際に発行されたクエリの内容を確認することが可能です。ただし、先ほどとは少しページの構成が異なっています。今回はデータベースに保存するのではなく、データベースから取得するためのクエリが発行されるため、このページからクエリの発行によって取得されたレコード(インスタンス)の情報が確認できるようになっています。
適当なクエリの行の Sel
ボタンをクリックすれば、
下の図のように、そのクエリの発行によって取得されたレコード一覧が表示されることになります。
テンプレート上でのクエリの発行タイミングも確認可能
また、+
ボタンをクリックすれば、そのクエリが発行されたタイミングを確認することができます。index
関数の場合、create
関数と違ってテンプレートファイルを利用していますが、この場合はテンプレートファイルのどこでクエリが発行されているのかを確認することが可能です。
このように、django-debug-toolbar
を利用すれば、ページを表示する際に発行されたクエリの内容や発行タイミングを確認することが可能です。
クエリの情報を利用してパフォーマンスを改善する
ただ、これらの情報をボーっと眺めていても意味はなく、これらの情報から、ウェブアプリのパフォーマンス改善ポイントを調べていくことが重要となります。
例えばですが、http://localhost:8000/appli/
を開いた際のクエリの情報には、下の図で示すようなオレンジ色で示した 10 similar queries
というメッセージが表示されていることを確認できると思います。
これはメッセージの通り、同じようなクエリが何回も発行されていることを示すメッセージであり、このクエリの発行に関しては改善可能かもしれないということを示唆しています。
このメッセージが表示されているクエリの発行タイミングを先ほどと同様の手順で調べると、発行タイミングは全てテンプレートファイル内における for
ループの中の処理実行時であり、for
ループの中で毎回インスタンスを取得するためのクエリが発行されていることを確認することができます。であれば、for
ループ外で一度に必要なインスタンスを全てごっそり取得しておけば、for
ループ内でのクエリの発行が不要となり、ウェブアプリのパフォーマンスが向上する可能性があります。
データベースに詳しい人であればもう察していらっしゃるかもしれませんが、今回作成した index
関数および index.html
は N + 1
問題が発生する作りとなっており、実は views.py
における index
関数の下記部分を、
comments = Comment.objects.all()
下記のように変更すれば、先ほど示した for
ループ内でのクエリの発行を 0
にすることができます。
comments = Comment.objects.select_related('user').all()
実際にクエリの発行内容を確認すれば、下の図のようにクエリの発行回数が 1
回のみになっていることが確認できると思います。
このように、クエリの内容や発行タイミングを確認することで、自身のウェブアプリのパフォーマンスを低下させる原因となるクエリの発行を見つけ出すことができ、それを解決することでウェブアプリのパフォーマンス(反応速度など)を向上させることが可能となります。
Django を利用しているとクエリの発行に関してはあまり意識しないかもしれないですが、django-debug-toolbar
を利用することで簡単にクエリの内容やクエリの発行タイミングを確認することができ、自身のウェブアプリがデータベースとどのようなやりとりをしているかを知ることができます。それを見るだけでも面白いのですが、パフォーマンス低下の原因につながるクエリが見つけ、それを改善していくことも重要となります。
その他の情報を確認する
ここまでクエリの情報の確認を行ってきましたが、みなさんお気づきの通り、ツールバーにはクエリの情報を確認する SQL
だけでなく様々な項目があります。これらの項目をクリックすることで、いろんな情報を確認することが可能となっています。
Settings
の内容を確認する
ツールバーをクリックして情報を表示してみれば、大体どんな情報が確認可能かは理解していただけるのではないかと思います。例えば Settings
をクリックすれば、プロジェクトの設定内容を全て確認することが可能となっています。
ここで表示されるのはプロジェクトの settings.py
で設定した項目だけでなく、プロジェクトの settings.py
では設定していない項目、つまりデフォルト値が指定されている項目についても確認することができます。
したがって、プロジェクトの settings.py
で設定した内容以外の設定も確認することができますし、これらは settings.py
で設定を上書きすることが可能な設定項目となっていますので、settings.py
で設定可能な項目も網羅的に知ることができます。
ちなみに、settings.py
で設定可能な項目については下記ページでも説明していますので、興味があればこちらも読んでみていただければと思います。
Profiling
の内容を確認する
また、ツールバーの一番下には Profiling
という項目があります。この Profiling
は、おそらくデフォルトでは無効化されていますが、チェックボックスを ON にすることで有効化することができます。
この 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
で確認できるクエリの内容の意味も理解できるようになると思います!