So, I started learning to code in Python and later Django. The first times it was hard looking at tracebacks and actually figure out what I did wrong and where the syntax error was. Some time has passed now and some way along the way, I guess I got a routine in debugging my Django code. As this was done early in my coding experience, I sat down and wondered if how I was doing this was ineffective and could be done faster. I usually manage to find and correct the bugs in my code, but I wonder if I should be doing it faster?

我通常只使用Django启用时提供的调试信息。当事情确实像我想象的那样结束时,我用一个语法错误破坏了代码流,并查看流中那个点的变量,以找出代码在哪里做了与我想要的不同的事情。

但这种情况还能改善吗?是否有一些更好的工具或方法来调试Django代码?


当前回答

我刚找到wdb (http://www.rkblog.rk.edu.pl/w/p/debugging-python-code-browser-wdb-debugger/?goback=%2Egde_25827_member_255996401)。它有一个非常漂亮的用户界面/ GUI,所有的铃铛和口哨。作者这样说wdb -

“像PyCharm这样的ide有自己的调试器。它们提供了相似或相同的功能……然而,要使用它们,你必须使用那些特定的ide(其中一些是非免费的,或者可能不适用于所有平台)。根据你的需要选择合适的工具。”

我想我应该把它传下去。

这也是一篇关于python调试器的非常有用的文章: https://zapier.com/engineering/debugging-python-boss/

最后,如果你想在Django中看到一个漂亮的调用堆栈的图形打印输出,checkout: https://github.com/joerick/pyinstrument。只需将pyinstrument.middleware.ProfilerMiddleware添加到MIDDLEWARE_CLASSES中,然后将?profile添加到请求URL的末尾以激活剖析器。

也可以从命令行或导入作为模块运行pyinstrument。

其他回答

使用PDB或ipdb。这两者的区别是ipdb支持自动完成。

pdb的

import pdb
pdb.set_trace()

对于ipdb

import ipdb
ipdb.set_trace()

执行换行按n键,继续按c键。 使用帮助(pdb)检查更多选项

正如在其他文章中提到的,在你的代码中设置断点,然后遍历代码,看看它是否像你预期的那样运行,这是学习像Django这样的东西的好方法,直到你对它的所有行为以及你的代码在做什么有很好的感觉。

要做到这一点,我建议使用WingIde。就像其他提到的ide一样,很好很容易使用,布局很好,也很容易设置断点,计算/修改堆栈等。完美的可视化你的代码正在做什么,因为你逐步通过它。我是它的超级粉丝。

我还使用PyCharm——它有出色的静态代码分析,有时可以帮助你在意识到问题之前发现问题。

如前所述,django-debug-toolbar是必不可少的- https://github.com/django-debug-toolbar/django-debug-toolbar

虽然不是一个明确的调试或分析工具,但我最喜欢的一个是SQL打印中间件,可以从Django Snippets (https://djangosnippets.org/snippets/290/)上获得

这将显示视图生成的SQL查询。这将使您很好地了解ORM正在做什么,以及您的查询是否有效,或者您是否需要重做代码(或添加缓存)。

我发现它对于在开发和调试应用程序时监视查询性能非常有用。

还有一个技巧——我为自己的使用对它进行了稍微修改,只显示摘要而不显示SQL语句....所以我总是在开发和测试时使用它。我还补充说,如果len(connection.queries)大于预定义的阈值,它会显示一个额外的警告。

然后,如果我发现发生了一些不好的事情(从性能或查询数量的角度来看),我会返回SQL语句的完整显示,以查看到底发生了什么。当你与多个开发人员一起开发一个大型Django项目时,这非常方便。

我已经把django-pdb推到了PyPI。 这是一个简单的应用程序,这意味着你不需要编辑你的源代码,每次你想进入pdb。

安装只是…

PIP安装django-pdb 添加'django_pdb'到你的INSTALLED_APPS

你现在可以运行:manage.py runserver——pdb在每个视图的开始进入pdb…

bash: manage.py runserver --pdb
Validating models...

0 errors found
Django version 1.3, using settings 'testproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

GET /
function "myview" in testapp/views.py:6
args: ()
kwargs: {}

> /Users/tom/github/django-pdb/testproject/testapp/views.py(7)myview()
-> a = 1
(Pdb)

然后运行:manage.py test——pdb在测试失败/错误时进入pdb…

bash: manage.py test testapp --pdb
Creating test database for alias 'default'...
E
======================================================================
>>> test_error (testapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../django-pdb/testproject/testapp/tests.py", line 16, in test_error
    one_plus_one = four
NameError: global name 'four' is not defined
======================================================================

> /Users/tom/github/django-pdb/testproject/testapp/tests.py(16)test_error()
-> one_plus_one = four
(Pdb)

该项目托管在GitHub上,当然欢迎贡献。

一个关于模板标签的小窍门:

@register.filter 
def pdb(element):
    import pdb; pdb.set_trace()
    return element

现在,在模板中,您可以执行{{template_var|pdb}}并进入pdb会话(假设您正在运行本地devel服务器),在那里您可以检查元素到您的心的内容。

这是一种很好的方式,可以看到当对象到达模板时发生了什么。

另外一个建议。

您可以同时利用nosetests和pdb,而不是手动在视图中注入pdb.set_trace()。这样做的好处是,您可以在错误条件第一次启动时观察到它们,可能是在第三方代码中。

这是我今天的一个错误。

TypeError at /db/hcm91dmo/catalog/records/

render_option() argument after * must be a sequence, not int

....


Error during template rendering

In template /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/crispy_forms/templates/bootstrap3/field.html, error at line 28
render_option() argument after * must be a sequence, not int
18  
19          {% if field|is_checkboxselectmultiple %}
20              {% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
21          {% endif %}
22  
23          {% if field|is_radioselect %}
24              {% include 'bootstrap3/layout/radioselect.html' %}
25          {% endif %}
26  
27          {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
28  

      {% if field|is_checkbox and form_show_labels %}

现在,我知道这意味着我把表单的构造函数弄糊涂了,我甚至很清楚哪个字段有问题。但是,我可以使用pdb看到脆皮的形式是抱怨,在一个模板?

是的,我会。在nosetests上使用——pdb选项:

测试$ nosetests test_urls_catalog.py——pdb

一旦我碰到任何异常(包括优雅地处理的异常),pdb就会停止,我可以四处查看。

  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 537, in __str__
    return self.as_widget()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 593, in as_widget
    return force_text(widget.render(name, self.value(), attrs=attrs))
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 513, in render
    options = self.render_options(choices, [value])
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 543, in render_options
    output.append(self.render_option(selected_choices, *option))
TypeError: render_option() argument after * must be a sequence, not int
INFO lib.capture_middleware log write_to_index(http://localhost:8082/db/hcm91dmo/catalog/records.html)
INFO lib.capture_middleware log write_to_index:end
> /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py(543)render_options()
-> output.append(self.render_option(selected_choices, *option))
(Pdb) import pprint
(Pdb) pprint.PrettyPrinter(indent=4).pprint(self)
<django.forms.widgets.Select object at 0x115fe7d10>
(Pdb) pprint.PrettyPrinter(indent=4).pprint(vars(self))
{   'attrs': {   'class': 'select form-control'},
    'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]],
    'is_required': False}
(Pdb)         

现在,很明显,我对crispy field构造函数的choices参数是一个列表中的列表,而不是元组中的list/tuple。

 'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]]

整洁的事情是,这个pdb发生在脆皮的代码,而不是我的,我不需要手动插入它。