Сигнал / Метод для каждого выполненного оператора SQL - PullRequest
12 голосов
/ 20 мая 2019

Согласно этой 12-летней проблеме, django не поддерживает сигнал для каждого выполненного оператора sql: https://code.djangoproject.com/ticket/5415

Мне нужно это в производственной среде, где debug = False.

Это означает, что перезапись connection.queries не работает.

Есть ли способ запустить какой-либо специальный код после каждого оператора sql (даже если debug = False)?

Ответы [ 3 ]

9 голосов
/ 23 мая 2019

Я взглянул на то, как Джанго населяет connection.queries.Когда DEBUG = True, базовый код базы данных, используемый всеми бэкэндами, оборачивает специфичные для базы данных курсоры с CursorDebugWrapper.В противном случае он использует CursorWrapper.Теоретически было бы возможно заставить Django заполнить его, даже когда DEBUG = False, переопределив свойство queries_logged или установив флаг force_debug_cursor на объекте соединения с базой данных.Любой метод заставит Django использовать CursorDebugWrapper, даже если DEBUG равен 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 для него выполнялся до того, как что-нибудь получит доступ к базе данных.

В сущности, это заменяет стандартные классы CursorWrapper и CursorDebugWrapper на пользовательские, которые позволяют знать, когдаSQL-запрос произошел.Это изменение вступает в силу для всех бэкэндов, которые поставляются с Django.

Я не знаю другого способа добавить эту возможность одним махом, как я делал выше.Сначала я посмотрел, смогу ли я получить новый бэкэнд из существующего.Я уверен, что это выполнимо, но для этого нужно много шаблонов.Кроме того, проекты, которые используют несколько бэкэндов одновременно, должны были бы получить новый бэкэнд для каждого бэкэнда запаса, который был первоначально использован.

3 голосов
/ 25 мая 2019

Я считаю, что интересное решение, опубликованное @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 имеет специальную панель с очень подробной информацией обо всей активности базы данных.

0 голосов
/ 26 мая 2019

Может быть, вы могли бы добавить logger к своим бэк-эндам БД :

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

Это будет отслеживать все транзакции базы данных.

Я помню, видел это несколько лет назад,но я думаю, что это должно сработать, поскольку система ведения журнала почти такая же.

Это не сигнал, но я думаю, что он будет делать то, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...