数据库工具

为了帮助你更好地理解和控制代码产生的数据库查询,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:要发送到数据库的SQL查询,类型为str。

  • 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()

connection.execute_wrapper(wrapper)

返回一个上下文管理器:进入时安装围绕数据库查询执行的包装器,退出时移除。包装器安装在线程本地的连接对象上。

wrapper是接受5个参数的可调用对象。在上下文管理器的作用域内,每次执行查询时都会调用它,参数依次为上面介绍的execute、sql、params、many和context。它应当调用execute(sql, params, many, context),并返回该调用的返回值。


原文:数据库工具;作者:Django Software Foundation与文档贡献者。版本或日期:Django6.1文档。原文及源码权利归相应权利人所有。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容