数据库监控¶
为了帮助您理解和控制代码发出的查询,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
是一个接受五个参数的可调用对象。它在上下文管理器的范围内为每次查询执行调用,参数为execute
、sql
、params
、many
和context
,如上所述。它应该调用execute(sql, params, many, context)
并返回该调用的返回值。