数据库工具¶
为了帮助你更好的理解并控制由你的代码所产生的数据库查询,Django提供了一个钩子函数,在这个钩子函数中你可以在数据库查询方法外层添加一层wrappers方法.举例说明, wrappers方法可以记录数据库查询的数量, 计算查询持续的事件, 为查询记录日志, 甚至可以阻止查询的执行(例如在渲染使用了预取的数据的模板时确保没有数据库查询被执行).
装饰器是在 middleware 之后建模的--它们是可调用的,并把其他调用作为它们的参数之一。它们调用可调用函数来调用(可能是包装的)数据库查询,并且它们可以围绕这个调用做一些工作。然而,它们通过用户代码来创建和安装,因此不需要独立像中间件这样的独立文件。
wrapper方法的安装是在上下文管理器中完成的 -- 因此wrapper方法是暂时的, 也是针对于你代码里的某些特定逻辑的.
正如上面提到的, 一个使用wrapper方法的例子是阻塞查询的执行. 类似的代码为:
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)
发给wrapper方法的参数是:
"execute" -- 一个可以被执行的对象, 使用剩下的参数触发来执行查询.
sql-- 一个str,要发送到数据库的SQL 查询。params-- SQL命令行参数值的列表/二元组,或者列表集/二元组集的一个列表/二元组(如果包装过的调用是executemany()的话)。many-- 一个布尔值,标识最终的调用是否是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 is a callable taking five arguments. It is called for every query
execution in the scope of the context manager, with arguments execute,
sql, params, many, and context as described above. It's
expected to call execute(sql, params, many, context) and return the return
value of that call.