Django:显示/记录来自 python shell 的 ORM sql 调用

Posted

技术标签:

【中文标题】Django:显示/记录来自 python shell 的 ORM sql 调用【英文标题】:Django: show/log ORM sql calls from python shell 【发布时间】:2011-01-19 20:55:00 【问题描述】:

使用出色的Django-Devserver,我在我的代码中发现了各种有趣且意想不到的 SQL 调用。我想找到调用的来源,因此我正在寻找一种方法来获取由 Python shell 中的 Django ORM 生成的所有 SQL 调用的日志或打印输出。也就是说,当我通过 Python shell 执行 Django ORM 调用时,我希望看到结果 SQL 被打印出来或记录下来。

我注意到了几种将日志信息添加到 html 页面的解决方案。有没有一种简单的方法可以转储到命令行?

【问题讨论】:

【参考方案1】:

Rob Hudson 的 Django Debug Toolbar 以及它的一般性能,还包括一个非常漂亮的 debugsqlshell manage.py 命令,它正是这样做的。

【讨论】:

要查找呼叫来自何处以及“幕后”发生了什么,我还推荐使用 Django 调试工具栏。不仅适用于 debugsqlshell 命令,还适用于实际工具栏,该工具栏也有大量关于 SQL 调用的信息。【参考方案2】:

如果你在 shell 中,或者在任何地方,你可以使用 queryset 方法

query.as_sql()

打印 SQL 命令。

即:

MyModel.objects.all().query.as_sql()

【讨论】:

你也可以这样做 >>> print MyModel.objects.all().query 使用 Django 1.5 和 query.as_sql() 对我不起作用。 query.sql_with_params() 确实如此。【参考方案3】:

如果您真的很想查看/记录所有 SQL 查询,您会想尝试 Django 1.3(目前处于 alpha 阶段,但即将投入生产),它为许多组件(包括数据库)启用 Python loggers后端。

当然,如果你坚持使用稳定版的 Django,你可以通过将 django/db/models/sql/compiler.py 添加到导入列表的底部来相对轻松地获得相同的效果:

import logging
_querylogger = logging.getLogger( 'sql.compiler' )

找到SQLCompiler::execute_sql()方法并更改:

    cursor = self.connection.cursor()
    cursor.execute( sql, params )

到这里:

    cursor = self.connection.cursor()
    _querylogger.info( "%s <= %s", sql, params )
    cursor.execute( sql, params )

【讨论】:

【参考方案4】:
qs = YourModel.objects.all()

qs.query.get_compiler('default').as_sql()

【讨论】:

str(qs) 也能胜任。【参考方案5】:

如果您使用的是 Django 1.3:

import logging
l = logging.getLogger('django.db.backends')
l.setLevel(logging.DEBUG)
l.addHandler(logging.StreamHandler())

【讨论】:

如果我使用 django 1.2 会怎样? +1 这也适用于 django 1.5,你这辈子都去哪儿了? 这对 Django 1.8 仍然有用。 Rails 控制台上的 ruby​​ 默认启用了这种输出,我希望复制它;太好了! 我应该把这段代码放在settings.py中吗?菜鸟在这里。请帮忙。【参考方案6】:

我试图在生产服务器上的 shell 中使用“Django: show/log ORM sql calls from python shell”,但它不起作用。最终有人指出它只会在DEBUG = True 时执行此调试日志记录。但是你可以像这样解决这个问题:

import logging
from django.db import connection
connection.force_debug_cursor = True  # Change to use_debug_cursor in django < 1.8
l = logging.getLogger('django.db.backends')
l.setLevel(logging.DEBUG)
l.addHandler(logging.StreamHandler())

我把它留在这里,以便我以后可以找到它,并希望它可以像我一样节省其他人的挖掘工作。

【讨论】:

【参考方案7】:

使用 django 扩展。

pip install django-extensions
./manage.py shell_plus --print-sql

对于生产环境,由于调试设置,它可能无法工作。

【讨论】:

即使在生产环境中,您也可以将settings.DEBUG=True 作为第一个 Python shell_plus 命令运行。 如果 Django 生成的查询太长(除非您的表只有几列),否则 shell 会停止打印并继续执行。我看到的所有查询都没有到达 WHERE 子句。【参考方案8】:

仅提及另一个选项 - 我制作了 django-sql-sniffer 工具,以便它可以附加到任何正在运行的 Python 进程并监视/分析来自 Django ORM 的 SQL 执行。

因此,它也可用于监控来自 Python shell 进程的查询(参见演示视频的github 页面):

打开 shell 并获取进程 ID:
In [1]: import os

In [2]: os.getpid()
Out[2]: 99688
在另一个选项卡中附加django-sql-sniffer(在这种情况下,指定尾部模式以实时跟踪查询)
$ django-sql-sniffer -p 99688 -t
返回您的 shell 并开始执行查询
In [3]: cat library/models.py
from django.db import models


class Book(models.Model):
    title = models.CharField(null=False, blank=False, max_length=100)
    author = models.ForeignKey('Author', on_delete=models.CASCADE)


class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)


In [4]: from library.models import *

In [5]: books = Book.objects.all()

In [6]: len(books)  # better use count
Out[6]: 121000

In [7]: book = books.first()  # query set not ordered, will hit db again

In [8]: book = books.first()  # query set not ordered, will hit db again

In [9]: book = books.first()  # query set not ordered, will hit db again

In [10]: book.author.id  # better use author_id
Out[10]: 1

In [11]: Book.objects.filter(author__first_name__startswith='A').count() > 0
Out[11]: True

In [12]: Book.objects.filter(author__first_name__startswith='A').exists()
Out[12]: True
该工具会在执行原始查询时打印它们
Count: 1; Duration: 0.002211809; Max Duration: 0.002211809; Query:
SELECT "library_book"."id",
       "library_book"."title",
       "library_book"."author_id"
  FROM "library_book"
-------------------------------------------------------
Count: 1; Duration: 0.000240326; Max Duration: 0.000240326; Query:
SELECT "library_book"."id",
       "library_book"."title",
       "library_book"."author_id"
  FROM "library_book"
 ORDER BY "library_book"."id" ASC
 LIMIT 1
-------------------------------------------------------
Count: 2; Duration: 0.000150919; Max Duration: 0.000240326; Query:
SELECT "library_book"."id",
       "library_book"."title",
       "library_book"."author_id"
  FROM "library_book"
 ORDER BY "library_book"."id" ASC
 LIMIT 1
-------------------------------------------------------
Count: 3; Duration: 0.000187874; Max Duration: 0.000240326; Query:
SELECT "library_book"."id",
       "library_book"."title",
       "library_book"."author_id"
  FROM "library_book"
 ORDER BY "library_book"."id" ASC
 LIMIT 1
-------------------------------------------------------
Count: 1; Duration: 0.000919104; Max Duration: 0.000919104; Query:
SELECT "library_author"."id",
       "library_author"."first_name",
       "library_author"."last_name"
  FROM "library_author"
 WHERE "library_author"."id" = %s
 LIMIT 21
-------------------------------------------------------
Count: 1; Duration: 0.040677071; Max Duration: 0.040677071; Query:
SELECT COUNT(*) AS "__count"
  FROM "library_book"
 INNER JOIN "library_author"
    ON ("library_book"."author_id" = "library_author"."id")
 WHERE "library_author"."first_name" LIKE %s ESCAPE '\'
-------------------------------------------------------
Count: 1; Duration: 0.002345800; Max Duration: 0.002345800; Query:
SELECT (1) AS "a"
  FROM "library_book"
 INNER JOIN "library_author"
    ON ("library_book"."author_id" = "library_author"."id")
 WHERE "library_author"."first_name" LIKE %s ESCAPE '\'
 LIMIT 1
-------------------------------------------------------
最后,点击Ctrl + C 停止该工具并分析所有捕获的查询
=======================================================

  ____    ___   _       ____  _____   _   _____  ____
 / ___|  / _ \ | |     / ___||_   _| / \ |_   _|/ ___|
 \___ \ | | | || |     \___ \  | |  / _ \  | |  \___ \
  ___) || |_| || |___   ___) | | | / ___ \ | |   ___) |
 |____/  \__\_\|_____| |____/  |_|/_/   \_\|_|  |____/

               Django SQL Sniffer v1.0.0
=======================================================
             TOP QUERIES BY MAX DURATION


Count: 1; Max Duration: 0.040677071; Combined Duration: 0.040677071; Query:
SELECT COUNT(*) AS "__count"
  FROM "library_book"
 INNER JOIN "library_author"
    ON ("library_book"."author_id" = "library_author"."id")
 WHERE "library_author"."first_name" LIKE %s ESCAPE '\'
-------------------------------------------------------
Count: 1; Max Duration: 0.002345800; Combined Duration: 0.002345800; Query:
SELECT (1) AS "a"
  FROM "library_book"
 INNER JOIN "library_author"
    ON ("library_book"."author_id" = "library_author"."id")
 WHERE "library_author"."first_name" LIKE %s ESCAPE '\'
 LIMIT 1
-------------------------------------------------------
Count: 1; Max Duration: 0.002211809; Combined Duration: 0.002211809; Query:
SELECT "library_book"."id",
       "library_book"."title",
       "library_book"."author_id"
  FROM "library_book"
-------------------------------------------------------
=======================================================

【讨论】:

以上是关于Django:显示/记录来自 python shell 的 ORM sql 调用的主要内容,如果未能解决你的问题,请参考以下文章

Python脚本之django---mysql-记录主机性能数据到数据库-web站点管理数据库及web显示命令执行结果

Pycharm没有显示来自django外部库的所有文件

python学习点滴记录-Day20(分页cookie/sessionajax)

Django动态表与AJAX

Django模型:如何避免在使用来自同一个表的2个外键时引用相同的记录

Django没有在404页面上应用来自应用程序的CSS文件