使用django很久了,但是对其原理一直不深究,只是看documentation,然后实现自己的功能模块而已。但是最近被老大说,在页面上做调试,不能进入代码断点调试是很不明智的行为。程序员必须学会调试断点啊,于是在网上搜索django如何进行调试的文章。
找到几篇,尝试了一下,原来也不是很麻烦。
下面是转自:http://www.54xue.com/w/98/n-3898.html的文章,讲的不错。 使用wingIDE调试django,搞定。但是之前是用pycharm来做的开发,pycharm没有找到写输入参数的地方,所以noreload不知道添加在哪里,回头再找找( ⊙o⊙ )哇.....
最近在研究Python,因为是搞Web编程的,所以就研究了django框架。看了django框架感觉很不错,于是按照惯性思维就想找个比较好点的IDE。于是在网上找了找,看了一些介绍,然后自己下载了几个IDE研究了一下。我大概研究了Pscripter、Eric还有PythonWin几个IDE,我个人比较注重调试技巧,于是研究了这几个IDE的调试功能,但是这几个IDE给我的感觉就是调试普通的Python程序还行,但是对于django的调试支持的不好。基本上都是在django的view上下一个断点,然后浏览器执行,但是程序根本就不会在断点那里停下来。
看来开源的东东还是不行,于是只好去下一个商业版的IDE看看了。看了一些介绍,我选择下载了WingIDE,然后开始调试django程序,发现和上面的IDE是一样的,断点那停不下来。一想,不对啊。一个商业IDE不可能不支持django调试啊,何况WingIDE首页上明确说支持django调试。没有什么能难倒我平一指的,于是我到WingIDE的网站上看了篇文章,终于了解了django的调试是怎么一回事了。想到前面在网上查找django调试,基本没有好的文章描述这个,所以写出来给大家参考参考。
要了解怎么调试,包括我前面提到的那些断点停不下来的怪异情况,得先了解一下django的工作方式。很多人和我刚开始想的一样,认为Python作为一门解释语言,是动态解释的,所以想当然就认为django下面,如果修改了一个Python源文件,在浏览器上一刷新马上就能看到改动效果。其他的动态语言Perl,Asp和PHP也确实是这么做的,但是django不是。django作为一个高性能的Web框架,它缓存了所有的Python文件,所以从django启动那一刻起,你如果再修改源文件,在网页上也是看不到修改的效果的。当然,这个方式性能是高,但是这个只适合网站上线和部署后的情况,如果我们正在开发,那么这个特性对开发人员来说是极其不方便的。因为你不得不为你每次的修改去重启django。为了解决这个问题,django内置了一个开发服务器,这个开发服务器可以用manage.py runserver来启动。所有的django开发人员都是先在这个开发服务器上开发完毕,然后发布上线的。所以我们需要调试的,也是这个开发服务器。那么我们来看看这个django开发服务器是怎么工作的。顺便说一下,我花了3个小时调试了这个开发服务器,所以知道他怎么工作的,如果大家感兴趣,自己去调试一下看看可能会得到更深的体会。
一旦用户用manage.py runserver启动开发服务器,就启动了一个django的进程,当然如果我们用ps或者任务管理器去查看的话,这其实是一个Python进程,不过他运行了manage.py而已。这个启动的django进程并没有直接就开始作为Http Server来Listen 8000端口,而是创建了一个和自己一模一样的子进程。为什么呢?因为django要解决我们前面提到的问题,也就是当开发人员每次修改Python源文件的时候,在网页上能够立刻看到效果。我们看看这个子进程都做了啥,这个子进程已启动会检查自己是否一个子进程,如果是的话开始调用一个inner_run的函数,这个函数开始Listen 8000端口,作为Http Server开始服务。如果开发人员没有Python代码,一切都OK,如果django父进程发现Python代码被修改,他会重启这个子进程,重装如Python代码,这样修改后的Python代码就会在网页上体现出来了。
知道了他的工作原理,我们很容易就发现前面调试django时的问题的原因了。因为我们调试的django进程是django的父进程,而这个父进程并不是真正的Http Server,真正的Http Server是子进程。所以我们在代码中下的断点是永远不会被运行的,因为代码是那个子进程中运行的。那么要解决这个调试的问题就有两个办法,第一个是让django不产生子进程,让父进程直接作为Http Server来运行。第二个就是让调试器调试那个子进程。其实这两个方法在WingIDE网站的帮助文档里面都有,现在我把这两个方法分开描述一下。
方法一、让django不产生子进程,让父进程直接作为Http Server来运行。幸运的是,在新版的django中,加入了--noreload选项可以让django这么做,如果是老版的django可能需要自己去修改源代码才能实现这个功能。我用的0.96版已经支持了这个选项,现在好多人都用上1.0版的啦,所以这个不是问题。所以现在我们为了要调试django,就要用调试器这么来启动开发服务器了manage.py runserver --noreload。启动以后,我们在代码中下断点,然后去浏览器上刷新,看到没有,WingIDE已经截获到这个断点了,下面就可以开始你的调试了。但是这个方法有个缺点,我们已经在上面讨论了,就是你修改了页面代码在浏览器上就看不到效果了。这个调试方法是通用的一个调试方法,基本上现在大部分的开源和商业IDE都支持这个调试方法。但是在这种调试模式下,WingIDE更人性化一些。如果它发现源代码被修改了,他会提醒开发人员重启django进程,或者干脆帮助开发直接重启django进程,这样被修改的源代码就被重新装人了。商业的IDE就是商业的啊,我试用了其他开源IDE,基本上都没有这么人性化的功能。
方法二、调试子进程。这个方法不通用,开源的IDE基本都不支持。商业的IDE比如WingIDE和KOMODO是支持的。具体的调试方式是这样的,需要在被调试的代码里面import一个特殊的模块,这个模块是调试器特有的一个代理,当这个模板被运行后,他会尝试连接调试器(也就是WingIDE),如果连接成功,就可以看到WingIDE进入调试模式。当然,我们还需要用让WingIDE启动这种调试模式,可以通过Edit|Preference打开系统的配置对话框,找到Debugger下的External/Remote选项卡,勾选Enable Passive Listen选项就可以了。这时WingIDE会在指定的端口侦听那个特殊模块的连接请求,一旦连接成功,WingIDE就会进入调试模式。这个特殊的模块叫wingdbstub,它位于WingIDE安装目录的根目录下面。你需要把wingdbstub.py这个文件拷贝到你的源代码目录下面,然后在你的被调试代码中import wingdbstub就可以了,所有在import wingdbstub后的代码都可以被调试,你可以在后面下个断点试一试看。按照WingIDE的帮助文档的提示,我在manage.py的第一行加入了import wingdbstub,然后在Shell中执行manage.py runserver(注意,要在WingIDE外面启动服务器,不要用WingIDE来启动服务器),并且成功的连接上了WingIDE。高兴的以为一切都搞定了,然后在我的View的源代码上下了个断点,发现还是不能在断点上停下来,这就证明我们的调试器又在调试那个父进程,而不是我们要调试的子进程。看来商业的文档也不可信啊。停下想了想,要调试子进程,只能在我前面提到的inner_run函数中import wingdbstub来连接调试器了,因为这个函数是真正的http server的执行函数。打开django的源代码django/core/management.py,找到inner_run函数,在这个函数的第一行import wingdbstub。这时我们发现View中的断点终于能断下来了,一切搞定。我用的django 0.96,inner_run函数位于management.py中1188行,其他版本的django可能有些不同。顺便说一下,商业软件Komodo同样支持方法二,不过它有它自己特有的调试代理模块,大家可以到Komodo的官方网站上找到答案。
我现在使用wingIDE来运行django的程序,输入参数:
runserver 0.0.0.0:80001 --noreload
可以做到断点调试了,还能指定端口。