Instrumentation de base de données

Pour vous aider à comprendre et à contrôler les requêtes produites par votre code, Django fournit un point d’accès pour installer des fonctions enveloppantes autour de l’exécution des requêtes de base de données. Par exemple, ces fonctions peuvent compter les requêtes, mesurer leur durée, les journaliser ou même empêcher leur exécution (par ex. pour s’assurer qu’aucune requête ne soit exécutée lors du rendu d’un gabarit avec des données préchargées.

Les fonctions enveloppantes sont structurées comme pour les intergiciels – il s’agit d’objets exécutables qui acceptent un autre exécutable dans l’un de leurs paramètres. Elles appellent ce dernier pour invoquer la requête de base de données (potentiellement enveloppée), et elles peuvent faire ce qu’elles veulent autour de cet appel. Elles sont cependant créées et installées par le code utilisateur, ce qui fait qu’elles n’ont pas besoin d’une fabrique distincte comme pour les intergiciels.

L’installation d’une fonction enveloppante se fait dans un gestionnaire de contexte, ce qui rend ces fonctions temporaires et spécifiques à un certain flux de votre code.

Comme mentionné ci-dessus, un exemple de fonction enveloppante est un bloqueur d’exécution de requête. Voici à quoi cela pourrait ressembler

def blocker(*args):
    raise Exception("No database access allowed here.")

Et cela serait utilisé dans une vue pour bloquer les requêtes provenant d’un gabarit, comme ceci

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)

Les paramètres envoyés à la fonction enveloppante sont :

  • execute – un objet exécutable, qui devrait être appelé avec le reste des paramètres afin d’exécuter la requête.
  • sql – une chaîne str, la requête SQL qui sera envoyée à la base de données.
  • params – une liste (ou tuple) de paramètres à destination de la commande SQL, ou une liste de listes si l’appel enveloppé est executemany().
  • many – une valeur booléenne indiquant si l’appel invoqué en définitive sera execute() ou executemany() (et si params doit être une liste de valeurs ou une liste de listes de valeurs).
  • context – un dictionnaire avec des données supplémentaires sur le contexte de l’invocation. Cela inclut les éléments connection et cursor.

En utilisant les paramètres, une version à peine plus complexe du bloqueur pourrait inclure le nom de la connexion dans le message d’erreur

def blocker(execute, sql, params, many, context):
    alias = context["connection"].alias
    raise Exception("Access to database '{}' blocked here".format(alias))

Pour un exemple plus complet, un journaliseur de requête pourrait ressembler à ceci

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)

Pour utiliser cela, il faut créer un objet de journalisation et l’installer comme enveloppeur

from django.db import connection

ql = QueryLogger()
with connection.execute_wrapper(ql):
    do_queries()
# Now we can print the log.
print(ql.queries)

connection.execute_wrapper()

execute_wrapper(wrapper)

Renvoie un gestionnaire de contexte qui, en entrée, installe la fonction d’enveloppement autour des exécutions de requêtes de base de données, et en sortie enlève cette fonction. La fonction d’enveloppement est installée sur l’objet connexion du fil d’exécution local.

wrapper est un objet exécutable acceptant cinq paramètres. Il est appelé lors de chaque exécution de requête dans la portée du gestionnaire de contexte, avec les arguments execute, sql, params, many et context, tels que décrits ci-dessus. Cet exécutable est censé appeler execute(sql, params, many, context) et renvoyer la valeur de renvoi de cet appel.

Back to Top