---
title: "数据库工具"
version: 4.2
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/4.2/topics/db/instrumentation/
canonical: https://djangodocs.dev/zh-hans/4.2/topics/db/instrumentation/
---
# 数据库工具

为了帮助你更好的理解并控制由你的代码所产生的数据库查询,Django提供了一个钩子函数,在这个钩子函数中你可以在数据库查询方法外层添加一层wrappers方法.举例说明, wrappers方法可以记录数据库查询的数量, 计算查询持续的事件, 为查询记录日志, 甚至可以阻止查询的执行(例如在渲染使用了预取的数据的模板时确保没有数据库查询被执行).

装饰器是在 [middleware](/zh-hans/4.2/topics/http/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` 是一个带有五个参数的可调用函数。它在上下文管理器的范围内被每个查询调用，带着上面所述的五个参数 `execute`, `sql`, `params`, `many`, and `context` 。它预计会调用 `execute(sql, params, many, context)` ，并且返回那个调用的返回值。
