数据库检测 — Django 文档

来自菜鸟教程
Django/docs/3.2.x/topics/db/instrumentation
跳转至:导航、​搜索

数据库检测

为了帮助您理解和控制代码发出的查询,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(),则为列表/元组列表/元组。
  • manybool 指示最终调用的调用是 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 是一个带有五个参数的可调用对象。 上下文管理器范围内的每个查询执行都会调用它,参数为 executesqlparamsmany 和 [ X143X]如上所述。 预计调用 execute(sql, params, many, context) 并返回该调用的返回值。