Comment puis-je voir les requêtes SQL brutes exécutées par Django?


308

Existe-t-il un moyen de montrer le SQL que Django exécute lors de l'exécution d'une requête?

Réponses:


373

Voir la FAQ de la documentation: " Comment puis-je voir les requêtes SQL brutes exécutées par Django? "

django.db.connection.queries contient une liste des requêtes SQL:

from django.db import connection
print(connection.queries)

Les ensembles de requêtes ont également un queryattribut contenant la requête à exécuter:

print(MyModel.objects.filter(name="my name").query)

Notez que la sortie de la requête n'est pas un SQL valide, car:

"Django n'interpole jamais réellement les paramètres: il envoie la requête et les paramètres séparément à l'adaptateur de base de données, qui effectue les opérations appropriées."

D'après le rapport de bogue de Django # 17741 .

Pour cette raison, vous ne devez pas envoyer de sortie de requête directement à une base de données.


13
Pour pérenniser cette réponse, vous devriez plutôt lier la version actuelle de la documentation de Django: docs.djangoproject.com/en/dev/faq/models/…
Andre Miller

5
Très bonne réponse. Cependant, il est recommandé d'utiliser la str()fonction Pythonian intégrée spécifiée , qui appelle la __str__()méthode interne . par exemple, str(MyModel.objects.filter(name="my name").query) je recommanderais également d'utiliser IPython et le shell Django de votre projet. L'achèvement des tabulations permet ensuite l'introspection des objets. Comme Django est connu pour ses schémas de nommage assertifs, cette méthodologie a tendance à être très utile.
Lorenz Lo Sauer

7
Notez que la sortie de querySQL n'est pas valide, car "Django n'interpole jamais réellement les paramètres: il envoie la requête et les paramètres séparément à l'adaptateur de base de données, qui effectue les opérations appropriées." Source: code.djangoproject.com/ticket/17741
gregoltsov

3
@AndreMiller Vous devez utiliser stable, non dev, pour créer un lien vers la version actuelle de Django, comme ceci: docs.djangoproject.com/en/stable/faq/models/…
Flimm

3
django.db.connection.queries renvoie une liste vide
fantastory

61

Les extensions Django ont une commande shell_plus avec un paramètreprint-sql

./manage.py shell_plus --print-sql

Dans django-shell, toutes les requêtes exécutées seront imprimées

Ex.:

User.objects.get(pk=1)
SELECT "auth_user"."id",
       "auth_user"."password",
       "auth_user"."last_login",
       "auth_user"."is_superuser",
       "auth_user"."username",
       "auth_user"."first_name",
       "auth_user"."last_name",
       "auth_user"."email",
       "auth_user"."is_staff",
       "auth_user"."is_active",
       "auth_user"."date_joined"
FROM "auth_user"
WHERE "auth_user"."id" = 1

Execution time: 0.002466s [Database: default]

<User: username>

1
Je l'utilise avec --print-sql ou avec SHELL_PLUS_PRINT_SQL = True et cela n'aide pas - je ne vois toujours pas les requêtes. une idée pourquoi? django 1.8
Dejell

1
Vous devez définir DEBUG = True dans votre settings.py pour voir les requêtes
Konstantin Voschanov

50

Jetez un œil à debug_toolbar , il est très utile pour le débogage.

La documentation et la source sont disponibles sur http://django-debug-toolbar.readthedocs.io/ .

Capture d'écran de la barre d'outils de débogage


1
debug_toolbar est particulièrement utile lorsque vous avez une requête qui échoue avec une erreur de syntaxe SQL; il affichera la dernière requête qui a tenté de s'exécuter (et a échoué), ce qui facilite le débogage.
scoopseven

La seule chose est que vous voyez des requêtes SQL sur le navigateur. Si vous exécutez des tests à partir du terminal et souhaitez le voir là-bas, ce n'est pas une solution viable. Toujours génial, je l'utilise encore aujourd'hui.
Erdin Eray

24
q = Query.objects.values('val1','val2','val_etc')

print q.query

réponse très simple! Nice
Espoir Murhabazi

Cette fonctionnalité a-t-elle été supprimée? Cela ne fonctionne pas quand je le fais m = MyModel.objects.get(...)suivi parm.query
sg

C'est parce que ce mn'est plus un ensemble de requêtes. Utilisez q = MyModel.objects.filter(...), alors q.query, alors m = q.get().
Brouwer

24

Aucune autre réponse ne couvre cette méthode, donc:

Je trouve que la méthode de loin la plus utile, la plus simple et la plus fiable est de demander à votre base de données. Par exemple, sur Linux pour Postgres, vous pourriez faire:

sudo su postgres
tail -f /var/log/postgresql/postgresql-8.4-main.log

Chaque base de données aura une procédure légèrement différente. Dans les journaux de base de données, vous verrez non seulement le SQL brut, mais toute configuration de connexion ou surcharge de transaction que Django place sur le système.


8
ne pas oublier de mettre log_statement='all'dans postgresql.confcette méthode.
RickyA

2
Vous pouvez le trouver postgresql.confen exécutantpsql -U postgres -c 'SHOW config_file'
kramer65

17

Bien que vous puissiez le faire avec le code fourni, je trouve que l'utilisation de l'application de barre d'outils de débogage est un excellent outil pour afficher les requêtes. Vous pouvez le télécharger depuis github ici .

Cela vous donne la possibilité d'afficher toutes les requêtes exécutées sur une page donnée ainsi que le temps nécessaire à la requête. Il résume également le nombre de requêtes sur une page ainsi que le temps total pour un examen rapide. C'est un excellent outil, lorsque vous voulez voir ce que fait Django ORM dans les coulisses. Il possède également de nombreuses autres fonctionnalités intéressantes que vous pouvez utiliser si vous le souhaitez.


2
Il me semble que c'est la meilleure version: github.com/django-debug-toolbar/django-debug-toolbar
philfreo

15

Une autre option, voir les options de journalisation dans settings.py décrites par cet article

http://dabapps.com/blog/logging-sql-queries-django-13/

debug_toolbar ralentit chaque chargement de page sur votre serveur de développement, la journalisation ne l'est pas donc c'est plus rapide. Les sorties peuvent être sauvegardées sur console ou fichier, donc l'interface utilisateur n'est pas aussi agréable. Mais pour les vues avec beaucoup de SQL, le débogage et l'optimisation des SQL peuvent prendre du temps via debug_toolbar, car chaque chargement de page est si lent.


Excellent! Bien que la barre d'outils soit superbe, je pense que cette réponse devrait être acceptée. C'est la solution que je voulais car elle permet à "manage.py runserver" de consigner SQL sur la console et cela fonctionne avec "manage.py migrate". Ce dernier m'a permis de voir que "on delete cascade" n'était définitivement pas défini lors de la création de mes tables. Il convient de noter que cette réponse est basée sur docs.djangoproject.com/en/1.9/topics/logging/…
LS

10

Si vous vous assurez que votre fichier settings.py a:

  1. django.core.context_processors.debug répertorié dans CONTEXT_PROCESSORS
  2. DEBUG=True
  3. vous IPdans le INTERNAL_IPStuple

Ensuite, vous devriez avoir accès à la sql_queriesvariable. J'ajoute un pied de page à chaque page qui ressemble à ceci:

{%if sql_queries %}
  <div class="footNav">
    <h2>Queries</h2>
    <p>
      {{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }}, {{sql_time_sum}} Time
    {% ifnotequal sql_queries|length 0 %}
      (<span style="cursor: pointer;" onclick="var s=document.getElementById('debugQueryTable').style;s.disp\
lay=s.display=='none'?'':'none';this.innerHTML=this.innerHTML=='Show'?'Hide':'Show';">Show</span>)
    {% endifnotequal %}
    </p>
    <table id="debugQueryTable" style="display: none;">
      <col width="1"></col>
      <col></col>
      <col width="1"></col>
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">SQL</th>
          <th scope="col">Time</th>
        </tr>
      </thead>
      <tbody>
        {% for query in sql_queries %}
          <tr class="{% cycle odd,even %}">
            <td>{{ forloop.counter }}</td>
            <td>{{ query.sql|escape }}</td>
            <td>{{ query.time }}</td>
          </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
{% endif %}

J'ai obtenu la variable sql_time_sumen ajoutant la ligne

context_extras['sql_time_sum'] = sum([float(q['time']) for q in connection.queries])

à la fonction de débogage dans django_src / django / core / context_processors.py.


1
Je viens d'essayer ceci, et (après avoir supprimé la partie sql_time_sum), j'ai obtenu: Aucun cycle nommé dans le modèle. «impair, pair» n'est pas défini - que me manque-t-il?
naufragé du

8

J'ai développé une extension à cet effet, afin que vous puissiez facilement mettre un décorateur sur votre fonction d'affichage et voir combien de requêtes sont exécutées.

À installer:

$ pip install django-print-sql

Pour utiliser comme gestionnaire de contexte:

from django_print_sql import print_sql

# set `count_only` to `True` will print the number of executed SQL statements only
with print_sql(count_only=False):

  # write the code you want to analyze in here,
  # e.g. some complex foreign key lookup,
  # or analyzing a DRF serializer's performance

  for user in User.objects.all()[:10]:
      user.groups.first()

Pour utiliser comme décorateur:

from django_print_sql import print_sql_decorator


@print_sql_decorator(count_only=False)  # this works on class-based views as well
def get(request):
    # your view code here

Github: https://github.com/rabbit-aaron/django-print-sql


3

Je pense que cela devrait fonctionner si vous utilisez PostgreSQL:

from django.db import connections
from app_name import models
from django.utils import timezone

# Generate a queryset, use your favorite filter, QS objects, and whatnot.
qs=models.ThisDataModel.objects.filter(user='bob',date__lte=timezone.now())

# Get a cursor tied to the default database
cursor=connections['default'].cursor()

# Get the query SQL and parameters to be passed into psycopg2, then pass
# those into mogrify to get the query that would have been sent to the backend
# and print it out. Note F-strings require python 3.6 or later.
print(f'{cursor.mogrify(*qs.query.sql_with_params())}')

Cela a fonctionné même en Python 2. Seul un refactorisateur comme print (cursor.mogrify (* qs.query.sql_with_params ())) est tout ce dont il a besoin.
iChux

IIRC Cursor.mogrify renvoie une chaîne, donc je suppose que l'utilisation de la chaîne f pour le formatage est superflue ..
chander

2

Ce qui suit renvoie la requête en tant que SQL valide, basé sur https://code.djangoproject.com/ticket/17741 :

def str_query(qs):
    """
    qs.query returns something that isn't valid SQL, this returns the actual
    valid SQL that's executed: https://code.djangoproject.com/ticket/17741
    """
    cursor = connections[qs.db].cursor()
    query, params = qs.query.sql_with_params()
    cursor.execute('EXPLAIN ' + query, params)
    res = str(cursor.db.ops.last_executed_query(cursor, query, params))
    assert res.startswith('EXPLAIN ')
    return res[len('EXPLAIN '):]

2

J'ai créé un petit extrait que vous pouvez utiliser:

from django.conf import settings
from django.db import connection


def sql_echo(method, *args, **kwargs):
    settings.DEBUG = True
    result = method(*args, **kwargs)
    for query in connection.queries:
        print(query)
    return result


# HOW TO USE EXAMPLE:
# 
# result = sql_echo(my_method, 'whatever', show=True)

Il prend comme fonction paramètres (contient des requêtes SQL) pour inspecter et args, les kwargs nécessaires pour appeler cette fonction. En conséquence, il renvoie la fonction renvoyée et imprime les requêtes SQL dans une console.


1

J'ai mis cette fonction dans un fichier util dans l'une des applications de mon projet:

import logging
import re

from django.db import connection

logger = logging.getLogger(__name__)

def sql_logger():
    logger.debug('TOTAL QUERIES: ' + str(len(connection.queries)))
    logger.debug('TOTAL TIME: ' + str(sum([float(q['time']) for q in connection.queries])))

    logger.debug('INDIVIDUAL QUERIES:')
    for i, query in enumerate(connection.queries):
        sql = re.split(r'(SELECT|FROM|WHERE|GROUP BY|ORDER BY|INNER JOIN|LIMIT)', query['sql'])
        if not sql[0]: sql = sql[1:]
        sql = [(' ' if i % 2 else '') + x for i, x in enumerate(sql)]
        logger.debug('\n### {} ({} seconds)\n\n{};\n'.format(i, query['time'], '\n'.join(sql)))

Ensuite, au besoin, je l'importe et l'appelle à partir de n'importe quel contexte (généralement une vue) nécessaire, par exemple:

# ... other imports
from .utils import sql_logger

class IngredientListApiView(generics.ListAPIView):
    # ... class variables and such

    # Main function that gets called when view is accessed
    def list(self, request, *args, **kwargs):
        response = super(IngredientListApiView, self).list(request, *args, **kwargs)

        # Call our function
        sql_logger()

        return response

C'est agréable de le faire en dehors du modèle, car si vous avez des vues API (généralement Django Rest Framework), cela s'applique également là-bas.


1

Pour Django 2.2:

Comme la plupart des réponses ne m'ont pas beaucoup aidé lors de l'utilisation ./manage.py shell. Enfin, j'ai trouvé la réponse. J'espère que cela aide quelqu'un.

Pour afficher toutes les requêtes:

from django.db import connection
connection.queries

Pour afficher une requête pour une seule requête:

q=Query.objects.all()
q.query.__str__()

q.queryjuste afficher l'objet pour moi. L'utilisation de la __str__()(représentation de chaîne) affiche la requête complète.


0

Afficher les requêtes à l' aide de django.db.connection.queries

from django.db import connection
print(connection.queries)

Accéder à une requête SQL brute sur un objet QuerySet

 qs = MyModel.objects.all()
 print(qs.query)

0

Juste pour ajouter, dans django, si vous avez une requête comme:

MyModel.objects.all()

faire:

MyModel.objects.all().query.sql_with_params()

pour obtenir la chaîne sql

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.