每个执行的SQL语句的信号/方法

时间:2019-05-20 12:55:37

标签: django

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

我在debug = False的生产环境中需要它。

这意味着覆盖连接。查询不起作用。

是否可以在每个sql语句之后运行一些自定义代码(即使debug = False)?

3 个答案:

答案 0 :(得分:9)

我研究了Django如何填充connection.queries。当DEBUG = True时,所有后端使用的基础数据库后端代码用CursorDebugWrapper包装特定于数据库的游标。否则,它将使用CursorWrapper。从理论上讲,即使覆盖queries_logged属性或在数据库连接对象上设置标志DEBUG = False,也可以强制Django填充force_debug_cursor。即使CursorDebugWrapperDEBUG,这两种方法都将强制Django使用False。但是,我不推荐这种方法,因为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文件将在访问数据库的任何内容之前执行。

这实际上是在用自定义类替换股票CursorWrapperCursorDebugWrapper类,这些类允许知道何时发生SQL查询。此更改对Django随附的所有后端均生效。

我不知道像我在上面那样一举添加这种功能的另一种方法。我首先查看是否可以从现有后端中派生出新后端。我确定它是可行的,但是需要很多样板。此外,同时使用多个后端的项目必须为每个最初使用的股票后端派生一个新的后端。

答案 1 :(得分:3)

我确实相信@Louis发布的有趣的解决方案应该标记为正确的解决方案。

话虽如此,如果您需要记录和分析所有已执行的查询,则可以考虑安装django-debug-toolbar并将其配置为在生产环境中可供超级用户或特定管理用户选择使用。

例如,将其放在myproject/settings.py中:

def show_toolbar(request):
    from constance import config
    if not config.DEBUG_SHOW_TOOLBAR:
        return False
    return request.user.is_superuser


DEBUG_TOOLBAR_CONFIG = {
    'SHOW_TOOLBAR_CALLBACK': 'myproject.settings.show_toolbar',
    'INTERCEPT_REDIRECTS': False,
}

django-debug-toolbar有一个特定的面板,其中包含有关所有数据库活动的非常详细的信息。

答案 2 :(得分:0)

也许您可以添加一个logger to your db backends

import logging
logger = logging.getLogger('django.db.backends')
logger.setLevel(logging.DEBUG) #set the log level you want
logger.addHandler(logging.StreamHandler())

这将跟踪所有数据库事务。

我记得几年前看到过这种情况,但是我认为它应该可以工作,因为日志记录系统几乎是相同的。

这不是一个信号,但是我认为它会做您想要的。