如何查看Django正在运行的原始SQL查询?

时间:2009-07-02 13:03:07

标签: sql django

有没有办法在执行查询时显示Django正在运行的SQL?

20 个答案:

答案 0 :(得分:302)

请参阅文档常见问题解答:“How can I see the raw SQL queries Django is running?

django.db.connection.queries包含SQL查询列表:

from django.db import connection
print connection.queries

查询集还包含一个query attribute,其中包含要执行的查询:

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

请注意,查询的输出不是有效的SQL,因为:

  

“Django从未实际插入参数:它将查询和参数分别发送到数据库适配器,执行适当的操作。”

来自Django错误报告#17741

因此,您不应将查询输出直接发送到数据库。

答案 1 :(得分:44)

看看 debug_toolbar ,它对调试非常有用。

文档和来源位于http://django-debug-toolbar.readthedocs.io/

Screenshot of debug toolbar

答案 2 :(得分:40)

Django-extensions的命令shell_plus带有参数print-sql

./manage.py shell_plus --print-sql

在django-shell中,将打印所有已执行的查询

例:

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>

答案 3 :(得分:17)

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

print q.query

答案 4 :(得分:16)

虽然您可以使用提供的代码来完成,但我发现使用调试工具栏应用程序是显示查询的绝佳工具。您可以从github here下载。

这使您可以选择显示在给定页面上运行的所有查询以及查询所花费的时间。它还总结了页面上的查询数量以及快速审核的总时间。当你想看看Django ORM在幕后做什么时,这是一个很棒的工具。它还有许多其他不错的功能,如果你愿意,你可以使用它们。

答案 5 :(得分:16)

没有其他答案涵盖此方法,所以:

我发现到目前为止最有用,最简单,最可靠的方法是询问你的数据库。例如,在Linux for Postgres上你可能会这样做:

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

每个数据库的程序略有不同。在数据库日志中,您不仅会看到原始SQL,还会看到django放置在系统上的任何连接设置或事务开销。

答案 6 :(得分:13)

另一个选项,请参阅本文所描述的settings.py中的日志记录选项

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

debug_toolbar会降低开发服务器上的每个页面加载速度,但日志记录不会更快。输出可以转储到控制台或文件,因此UI不是很好。但对于包含大量SQL的视图,由于每个页面加载速度很慢,因此通过debug_toolbar调试和优化SQL可能需要很长时间。

答案 7 :(得分:10)

如果您确保您的settings.py文件包含:

    {li> django.core.context_processors.debug列在CONTEXT_PROCESSORS
  1. DEBUG=True
  2. IP元组中的INTERNAL_IPS
  3. 然后您应该可以访问sql_queries变量。我在每个页面上添加一个页脚,如下所示:

    {%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 %}
    

    我通过添加行

    得到了变量sql_time_sum
    context_extras['sql_time_sum'] = sum([float(q['time']) for q in connection.queries])
    

    到django_src / django / core / context_processors.py中的调试函数。

答案 8 :(得分:5)

我为此开发了一个扩展,因此您可以轻松地在视图函数上放置一个装饰器,并查看执行了多少查询。

安装:

$ pip install django-print-sql

用作上下文管理器:

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()

用作装饰者:

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

答案 9 :(得分:5)

如果您有类似这样的查询,只需添加django:

MyModel.objects.all()

做:

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

获取sql字符串

答案 10 :(得分:2)

Django SQL Sniffer 是另一种用于查看(并查看其统计信息)来自使用 Django ORM 的任何进程的原始执行查询的替代方法。我构建它是为了满足我所拥有的一个特定用例,我在任何地方都没有看到,即:

  • 无需更改目标进程正在执行的源代码(无需在 django 设置中注册新应用,无需到处导入装饰器等)
  • 日志配置没有变化(例如,因为我对一个特定进程感兴趣,而不是配置适用的整个进程组)
  • 不需要重启目标进程(例如,因为它是一个重要的组件,重启可能会导致一些停机时间)

因此,Django SQL Sniffer 可以临时使用,并附加到已经运行的进程。然后该工具“嗅探”执行的查询并在执行时将它们打印到控制台。当该工具停止时,系统会根据一些可能的指标(计数、最大持续时间和总持续时间)显示带有异常查询的统计摘要。

这是我附加到 Python shell 的示例的屏幕截图 enter image description here

您可以在 github page 上查看现场演示和更多详细信息。

答案 11 :(得分:1)

如果您使用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())}')

答案 12 :(得分:1)

对于Django 2.2:

使用./manage.py shell时,大多数答案对我没有多大帮助。终于我找到了答案。希望这对某人有帮助。

要查看所有查询,请执行以下操作:

from django.db import connection
connection.queries

要查看单个查询的查询,请执行以下操作:

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

q.query只是为我显示对象。 使用__str__()(字符串表示形式)显示完整的查询。

答案 13 :(得分:1)

以下内容根据https://code.djangoproject.com/ticket/17741

将查询返回为有效的SQL
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 '):]

答案 14 :(得分:1)

从django获取结果查询到数据库(使用正确的参数替换) 你可以使用这个功能:

from django.db import connection

def print_database_query_formatted(query):
    sql, params = query.sql_with_params()
    cursor = connection.cursor()
    cursor.execute('EXPLAIN ' + sql, params)
    db_query = cursor.db.ops.last_executed_query(cursor, sql, params).replace('EXPLAIN ', '')

    parts = '{}'.format(db_query).split('FROM')
    print(parts[0])
    if len(parts) > 1:
        parts = parts[1].split('WHERE')
        print('FROM{}'.format(parts[0]))
        if len(parts) > 1:
            parts = parts[1].split('ORDER BY')
            print('WHERE{}'.format(parts[0]))
            if len(parts) > 1:
                print('ORDER BY{}'.format(parts[1]))

# USAGE
users = User.objects.filter(email='admin@admin.com').order_by('-id')
print_database_query_formatted(users.query)

输出示例

SELECT "users_user"."password", "users_user"."last_login", "users_user"."is_superuser", "users_user"."deleted", "users_user"."id", "users_user"."phone", "users_user"."username", "users_user"."userlastname", "users_user"."email", "users_user"."is_staff", "users_user"."is_active", "users_user"."date_joined", "users_user"."latitude", "users_user"."longitude", "users_user"."point"::bytea, "users_user"."default_search_radius", "users_user"."notifications", "users_user"."admin_theme", "users_user"."address", "users_user"."is_notify_when_buildings_in_radius", "users_user"."active_campaign_id", "users_user"."is_unsubscribed", "users_user"."sf_contact_id", "users_user"."is_agree_terms_of_service", "users_user"."is_facebook_signup", "users_user"."type_signup" 
FROM "users_user" 
WHERE "users_user"."email" = 'admin@admin.com' 
ORDER BY "users_user"."id" DESC

它基于此票证评论: https://code.djangoproject.com/ticket/17741#comment:4

答案 15 :(得分:0)

我已经制作了一个你可以使用的小片段:

fafa
fafa

它需要参数函数(包含sql查询)来检查和args,需要调用该函数的kwargs。结果它返回什么函数返回并在控制台中打印SQL查询。

答案 16 :(得分:0)

我将此功能放在项目中一个应用程序的util文件中:

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)))

然后,在需要时,我将其导入并从任何需要的上下文(通常是视图)中调用它,例如:

# ... 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

最好在模板外部执行此操作,因为如果您具有API视图(通常是Django Rest Framework),那么它也适用于此。

答案 17 :(得分:0)

使用django.db.connection.queries查看查询

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

访问QuerySet对象上的原始SQL查询

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

答案 18 :(得分:0)

CREATE / UPDATE 命令生成 SQL,这些命令在 Django 中立即 [在 1.8 上测试]

def generate_update_sql(queryset, update_kwargs):
    """Converts queryset with update_kwargs
    like if was: queryset.update(**update_kwargs)

    qs = Event.objects.exclude(app='some')
    update_kwargs = dict(description='test', action='action')
    generate_update_sql(qs, update_kwargs) will return
    UPDATE `api_event` SET `description` = test, `action` = action WHERE NOT (`api_event`.`app` = some)
    """
    from django.db.models import sql

    query = queryset.query.clone(sql.UpdateQuery)
    query.add_update_values(update_kwargs)
    compiler = query.get_compiler(queryset.db)
    sql, params = compiler.as_sql()
    return sql % params
def generate_create_sql(model, model_data):
    """Converts queryset with create_kwargs
    like if was: queryset.create(**create_kwargs)
    
    generate_create_sql(Event, dict(slug='a', app='b', model='c', action='e')) will return
    "INSERT INTO `api_event` (`slug`, `app`, `model`, `action`, `action_type`) VALUES (a, b, c, e, )"
    
    """
    from django.db.models import sql

    not_saved_instance = model(**model_data)
    not_saved_instance._for_write = True

    query = sql.InsertQuery(model)

    fields = [f for f in model._meta.local_concrete_fields if not isinstance(f, AutoField)]
    query.insert_values(fields, [not_saved_instance], raw=False)

    compiler = query.get_compiler(model.objects.db)
    sql, params = compiler.as_sql()[0]
    return sql % params

测试和使用

    def test_generate_update_sql_with_F(self):
        qs = Event.objects.all()
        update_kwargs = dict(description=F('slug'))
        result = generate_update_sql(qs, update_kwargs)
        sql = "UPDATE `api_event` SET `description` = `api_event`.`slug`"
        self.assertEqual(sql, result)

    def test_generate_create_sql(self):
        result = generate_create_sql(Event, dict(slug='a', app='b', model='c', action='e'))
        sql = "INSERT INTO `api_event` (`slug`, `app`, `model`, `action`, `action_type`, `description`) VALUES (a, b, c, e, , )"
        self.assertEqual(sql, result)

答案 19 :(得分:-1)

只需执行以下操作,我就可以查看失败的查询:

tail -f /var/log/postgresql/*

假设Postgres 10.6,Ubuntu 18.04 +,python3 +,django2 +,并且已在postgres中启用日志记录。