Я взглянул на то, как Джанго населяет 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.
Я не знаю другого способа добавить эту возможность одним махом, как я делал выше.Сначала я посмотрел, смогу ли я получить новый бэкэнд из существующего.Я уверен, что это выполнимо, но для этого нужно много шаблонов.Кроме того, проекты, которые используют несколько бэкэндов одновременно, должны были бы получить новый бэкэнд для каждого бэкэнда запаса, который был первоначально использован.