数据库监控

为了帮助您理解和控制代码发出的查询,Django 提供了一个挂钩,用于在数据库查询执行周围安装包装函数。例如,包装器可以计数查询、测量查询持续时间、记录查询,甚至可以阻止查询执行(例如,确保在使用预取数据渲染模板时不发出任何查询)。

这些包装器模仿了中间件——它们是可以调用的对象,其中一个参数是另一个可调用对象。它们调用该可调用对象来调用(可能已包装的)数据库查询,并且它们可以在该调用周围执行任何操作。但是,它们是由用户代码创建和安装的,因此不需要像中间件那样单独的工厂。

包装器的安装是在上下文管理器中完成的——因此包装器是临时的,并且特定于代码中的一些流程。

如上所述,包装器的一个示例是查询执行阻止程序。它可能如下所示

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 – 一个str,要发送到数据库的 SQL 查询。

  • params – SQL 命令的参数值列表/元组,如果包装的调用是executemany(),则为列表/元组的列表/元组。

  • many – 一个bool,指示最终调用的调用是execute()还是executemany()(以及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是一个接受五个参数的可调用对象。它在上下文管理器的范围内为每次查询执行调用,参数为executesqlparamsmanycontext,如上所述。它应该调用execute(sql, params, many, context)并返回该调用的返回值。

返回顶部