首页 > 解决方案 > 每个执行的 sql 语句的信号/方法

问题描述

根据这个 12 年的问题,django 不支持每个执行的 sql 语句的信号:https ://code.djangoproject.com/ticket/5415

我在调试 = False 的生产环境中需要这个。

这意味着覆盖 connection.queries 不起作用。

有没有办法在每个 sql 语句之后运行一些自定义代码(即使 debug=False)?

标签: django

解决方案


我已经查看了 Django 的填充方式connection.queries。当 时DEBUG = True,所有后端使用的基本数据库后端代码用 包装特定于数据库的游标CursorDebugWrapper。否则,它使用CursorWrapper. 理论上,即使DEBUG = False通过覆盖属性或在数据库连接对象上queries_logged设置标志,也可以强制 Django 填充它。force_debug_cursor任何一种方法都会强制 Django 使用,CursorDebugWrapper即使DEBUGFalse. 但是,我不推荐这种方法,因为CursorDebugWrapper在您只想知道查询已执行的情况下,这种方法并不是特别有效。例如,除了填充connection.queries它之外,它还会将查询记录到记录器中。如果您不需要此日志记录,那么这只是一种浪费。

因此,从工作原理中汲取灵感CursorDebugWrapper,我想出了一种自定义方法来了解何时进行了 SQL 查询。

我创建了一个应用程序first,名为__init__.py

from django.db.backends import utils
from contextlib import contextmanager

# This is inspired by Django's stock CursorDebugWrapper class.
class Mixin(object):

    def execute(self, sql, params=None):
        with self.notify(sql, params, use_last_executed_query=True):
            return super().execute(sql, params)

    def executemany(self, sql, param_list):
        with self.notify(sql, param_list):
            return super().executemany(sql, param_list)

    @contextmanager
    def notify(self, sql=None, params=None, use_last_executed_query=False):
        try:
            yield
        finally:
            if use_last_executed_query:
                sql = self.db.ops.last_executed_query(self.cursor, sql, params)
            # I've used print for this proof-of-concept, replace with whatever
            # mechanism suits you.
            print("Executed: ", sql)

class CustomWrapper(Mixin, utils.CursorWrapper):
    pass

class CustomDebugWrapper(Mixin, utils.CursorDebugWrapper):
    pass

utils.CursorWrapper = CustomWrapper
utils.CursorDebugWrapper = CustomDebugWrapper

然后在我的settings.py文件中,我将first应用程序放在首位INSTALLED_APPS。这使得它的__init__.py文件在访问数据库的任何内容之前执行。

这实际上是用允许知道 SQL 查询何时发生的自定义类替换库存CursorWrapper和类。CursorDebugWrapper此更改对 Django 附带的所有后端生效。

我不知道另一种方法可以像我在上面所做的那样一举添加此功能。我首先查看是否可以从现有后端派生新后端。我确信这是可行的,但它需要大量的样板文件。此外,一次使用多个后端的项目必须为最初使用的每个库存后端派生一个新的后端。


推荐阅读