Instrumentering av databaser¶
För att hjälpa dig att förstå och kontrollera de frågor som utfärdas av din kod tillhandahåller Django en krok för att installera omslagsfunktioner runt exekveringen av databasfrågor. Till exempel kan wrappers räkna frågor, mäta frågans varaktighet, logga frågor eller till och med förhindra frågeexekvering (t.ex. för att se till att inga frågor utfärdas när du renderar en mall med förhämtade data).
Omslagen är modellerade efter middleware – de är anropsbara som tar en annan anropsbar som ett av sina argument. De anropar den anropsbara för att anropa databasfrågan (eventuellt inkapslad), och de kan göra vad de vill runt det anropet. De skapas och installeras dock av användarkoden och behöver därför inte en separat fabrik som middleware gör.
Installationen av en wrapper görs i en kontexthanterare - så wrappers är tillfälliga och specifika för ett visst flöde i din kod.
Som nämnts ovan är ett exempel på en wrapper en query execution blocker. Det skulle kunna se ut så här:
def blocker(*args):
raise Exception("No database access allowed here.")
Och det skulle användas i en vy för att blockera frågor från mallen så här:
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)
De parametrar som skickas till wrappers är:
execute
– en anropsbar, som ska anropas med resten av parametrarna för att utföra frågan.sql
– enstr
, SQL-frågan som ska skickas till databasen.params
– en lista/tupel av parametervärden för SQL-kommandot, eller en lista/tupel av listor/tuplar om det omslutna anropet ärexecutemany()
.many
– enbool
som anger om det sista anropet ärexecute()
ellerexecutemany()
(och omparams
förväntas vara en sekvens av värden, eller en sekvens av sekvenser av värden).context
– en ordbok med ytterligare data om kontexten för anropet. Detta inkluderar anslutningen och markören.
Med hjälp av parametrarna kan en något mer komplex version av blockeraren inkludera anslutningsnamnet i felmeddelandet:
def blocker(execute, sql, params, many, context):
alias = context["connection"].alias
raise Exception("Access to database '{}' blocked here".format(alias))
För ett mer komplett exempel kan en frågeloggare se ut så här:
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)
För att använda detta skapar du ett logger-objekt och installerar det som en wrapper:
from django.db import connection
ql = QueryLogger()
with connection.execute_wrapper(ql):
do_queries()
# Now we can print the log.
print(ql.queries)
anslutning.execute_wrapper()
¶
- execute_wrapper(wrapper)¶
Returnerar en kontexthanterare som, när den anges, installerar en omslagsdel runt exekveringar av databasfrågor, och när den avslutas, tar bort omslagsdelen. Omslaget installeras på det trådlokala anslutningsobjektet.
wrapper
är en anropbar funktion som tar fem argument. Den anropas för varje frågeexekvering i kontexthanterarens omfattning, med argumenten execute
, ql
, params
, many
och context
enligt beskrivningen ovan. Den förväntas anropa execute(sql, params, many, context)
och returnera returvärdet av det anropet.