前言
自己动手丰衣足食
之前在 思否 提了这个问题,但是没人回答啊,那就只能自己解决了!
不仅仅是思否,stackoverflow 不过都没有人回答
如何输出 SQL 语句
在项目的 settings.py
文件中添加如下内容就可以将一切对数据库的操作翻译为 SQL
语句,但是注意这个模式只有在 settings.py
中的 DEBUG
开关为 True
是才有效!
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
在单元测试中输出 SQL 语句的需求
上面的语句在做单元测试的时候就变得不好使!
根本就不输出 SQL
语句!!!
我一开始以为是上面代码中的 level
设置的不对,应该将 DEBUG
改为 生产,当然,答案并非如此!
找寻答案
为什么会有这个需求呢?其实还是为了调试代码方便嘛,但既然是调试代码,如果涉及数据库的操作却不输出 SQL
语句,就感觉是雾里看花、黑箱操作让人摸不着头脑!!!作为一名优秀的程序员怎么能允许这种事情发生呢?当然不可以!!!
所以我去通读了 Django
官方文档中相关的内容
-
通过阅读改文档可知:做单元测试的时候会以生产模式运行,这就解释了为什么不输出 SQL 语句,因为我们的日志设置的级别是 DEBUG
无论配置文件中的
DEBUG
设置值是多少,所有的 Django 测试都以DEBUG
=False 运行。这是为了确保你的代码观察到的输出与生产环境下的输出一致。 -
好的,找到了改进方向了,那就
level
改为生产模式就好了嘛!但是通过阅读文档发现只有:
DEBUG
:排查故障时使用的低级别系统信息INFO
:一般的系统信息WARNING
:描述系统发生了一些小问题的信息ERROR
:描述系统发生了大问题的信息CRITICAL
:描述系统发生严重问题的信息
这五种模式,最低就是 DEBUG,根本没有生产模式啊!!!
-
读完该篇文档,我懂了,之前想要修改
level
的想法是错误的,我们可以也应该通过自定义测试器 的方式来让单元测试也输出 SQL 语句!!!
说干就干!
说干就干
在根目录下面新建一个 testing 文件夹,然后在其中创建一个 testcases.py 文件,在其中写下如下的代码:
from django.test.runner import DiscoverRunner
class DebugDiscoverRunner(DiscoverRunner):
def __init__(self, *args, **kwargs):
super().__init__(debug_sql=True, verbosity=2)
这段代码有几个关键点
-
继承
DiscoverRunner
这个没什么好说的,默认就是
DiscoverRunner
,所以我们继承他 -
debug_sql=True, verbosity=2
继承它不为别的,就像开启上面的两个参数
testing
和testcases.py
这些名字随意,只是我习惯这么放置和取名字,你随意!!!
然后再 settings.py
文件中的任意位置添加下面的代码:
TEST_RUNNER = 'testing.testrunner.DebugDiscoverRunner'
这个时候你只需要再终端输入
python manage.py test
就会调用我们的 DebugDiscoverRunner
来做单元测试,执行 __init__.py
传递 debug_sql=True, verbosity=2
,就会再控制台源源不断的打印 SQL 语句!!!
一些细节
你会发现 DebugDiscoverRunner
的 __init__.py
传递给 super().__init__(debug_sql=True, verbosity=2)
的参数没有了 *args, **kwargs
,为什么呢?
因为会报错!!!
如下:
vagrant@vagrant:/vagrant$ python manage.py test comments
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
File "manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 23, in run_from_argv
super().run_from_argv(argv)
File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 330, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 371, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 52, in handle
test_runner = TestRunner(**options)
File "/vagrant/testing/testrunner.py", line 6, in __init__
super(DebugDiscoverRunner, self).__init__(debug_sql=True, verbosity=2, *args, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'verbosity'
参考文章:Django:DiscoverRunner覆盖引发错误
参考归参考,但是这个人的处理方法很不好
所以我们去掉了 *args, **kwargs