数据库检测

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

包装机是仿照 middleware --它们是可调用的,将另一个可调用作为它们的参数之一。他们调用这个可调用的来调用(可能是包装的)数据库查询,并且他们可以围绕这个调用做他们想要的事情。但是,它们是由用户代码创建和安装的,因此不需要像中间件那样的单独工厂。

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

如上所述,包装器的一个例子是查询执行拦截器。看起来像这样:

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

  • params --SQL命令的参数值列表/元组,或者列表/元组的列表/元组(如果包装调用是 executemany() .

  • many ——A 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)

要使用它,您将创建一个logger对象并将其作为包装器安装::

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) 并返回该调用的返回值。