データベースの計測

コードから発行されたクエリを理解して制御するために、Django は、データベースクエリの実行周辺にラッパー関数をインストールするためのフックを提供しています。たとえば、ラッパーは、クエリのカウント、クエリの実行時間の測定、クエリのログ、さらにクエリの実行の防止までもできます (たとえば、プリフェッチされたデータを使用してテンプレートをレンダリングする間にクエリが発行されないようにするため)。

ラッパーは ミドルウェア にならったもので、別の呼び出し可能オブジェクトを引数の1つとして受け取ります。ラッパーはデータベースクエリ(ラップされている可能性があります)を呼び出すために callable を呼び出します。しかし、これらはユーザーコードによって作成され、インストールされるため、ミドルウェアのように個別のファクトリを必要としません。

ラッパーのインストールは、コンテキストマネージャで行われます。そのため、ラッパーは一時的なもので、コード内の特定のフローに特化したものです。

前述したように、ラッパーの例はクエリ実行ブロッカーです。以下のようなものです:

def blocker(*args):
    raise Exception("No database access allowed here.")

そして、テンプレートからのクエリをブロックするには、ビューでこのように使用します:

from django.db import connection
from django.shortcuts import render


def my_view(request):
    context = {...}  # Code to generate context with all data.
    template_name = ...
    with connection.execute_wrapper(blocker):
        return render(request, template_name, context)

ラッパーに送られるパラメーターは以下の通りです:

  • execute -- クエリを実行するために、残りのパラメータと一緒に呼び出される呼び出し可能オブジェクト。
  • sql -- データベースに送信する SQL クエリの str
  • params -- SQL コマンドのパラメータ値のリストまたはタプル、または、ラップされた呼び出しが executemany() の場合はリストまたはタプルのリストまたはタプル。
  • many -- 最終的に呼び出すのが execute() なのか executemany() なのかを示す bool (params が値のシーケンスなのか、値のシーケンスのシーケンスなのかを示します)。
  • context -- 呼び出しのコンテキストに関するデータを含む辞書。これにはコネクションとカーソルが含まれます。

このパラメータを使用することで、次のように、もう少し複雑なバージョンのブロッカーで、エラーメッセージにコネクション名を含めることができます。

def blocker(execute, sql, params, many, context):
    alias = context["connection"].alias
    raise Exception("Access to database '{}' blocked here".format(alias))

より完全な例として、クエリロガーは次のようになります。

import time


class QueryLogger:
    def __init__(self):
        self.queries = []

    def __call__(self, execute, sql, params, many, context):
        current_query = {"sql": sql, "params": params, "many": many}
        start = time.monotonic()
        try:
            result = execute(sql, params, many, context)
        except Exception as e:
            current_query["status"] = "error"
            current_query["exception"] = e
            raise
        else:
            current_query["status"] = "ok"
            return result
        finally:
            duration = time.monotonic() - start
            current_query["duration"] = duration
            self.queries.append(current_query)

これを使用するには、ロガーオブジェクトを作成し、ラッパーとしてインストールします。

from django.db import connection

ql = QueryLogger()
with connection.execute_wrapper(ql):
    do_queries()
# Now we can print the log.
print(ql.queries)

connection.execute_wrapper()

execute_wrapper(wrapper)

入力すると、データベースクエリ実行の周囲にラッパーをインストールし、終了するとラッパーを削除するコンテキストマネージャを返します。ラッパーはスレッドローカルのコネクションオブジェクトにインストールされます。

wrapper は5つの引数を取る呼び出し可能オブジェクトです。 上記のように、 execute, sql, params, many, context を引数として、コンテキストマネージャのスコープ内でクエリを実行するたびに呼び出されます。 execute(sql、params、many、context) を呼び出して、その戻り値を返すことが期待されます。

Back to Top