2007年10月24日

Django的最佳系统结构

作者 非鱼

Django也用了一段时间了,写了两三个小网站,但是始终感觉自己写出来的站点目录和功能的安排还是比较混乱,很难达到让自己满意的效果,更不要说令人赏心悦目了。尤其是,当你需要开发下一个网站的时候,虽然感觉用户部分的功能(注册/登录/忘记密码/修改用户信息)所有的网站是通用的,但是想复用现有网站的这个功能却相当困难,居然笨到只能把模板文件/Model/View挨个复制过去再修改,实在是难登大雅之堂。

于是狠命的研究了一些文章,终于算是找到了点前人的经验之谈,大概的总结如下:

  • 项目文件manage.py/urls.py/settings.py尽量少的改动(当然,不改动也是不可能的),setting.py里面需要设置数据库的相关信息,还有模板目录之类的,模板目录是可以使用相对目录的(使用os.path),很可惜,我没有测试成功,在lighttpd下面它仍然使用相对目录来搜索模板,所以总是报错,但是我找到了另一种解决方案。
  • setting.py里面有一个配置选项TEMPLATE_LOADERS。默认情况下它使用两种加载机制,第一种是文件系统方式,即使用下面配置的TEMPLATE_DIRS目录,在里面寻找模板文件,如果没有找到,第二种是app模式,它会在INSTALLED_APPS所标识的已安装的App下面寻找templates目录,并在其中寻找模板文件。而这个第二种,跟TEMPLATE_DIRS是无关的。因此,只要注释掉第一行,TEMPLATE_DIRS这个选项就可以留空了。然后,在任意一个App目录下面建一个templates目录,把模板文件扔进去就OK了。当然,最佳方案是,每个Apps下面放它自己用到的模板文件。这样,以后将这个App放进其它的项目的时候,你不需要做任何设置,模板这一块就已经正常工作了。
  • 然后是Urls,在每个App下面添加它自己的urls.py文件,在里面设定它所用到的url映射,然后在项目级的urls.py里面使用include方式加载各个App的url配置就可以了。但是这样有一个要求,就是这个App要使用统一的目录前缀,比如用户相关的Url都以user/开头。(我不知道是不是必须这样,但是目前我所掌握的知识,只能这样了。)

通过这两个改动,已经将App和项目的耦合性降到了最低,现在如果要重用一个App,比如用户部分,只要把user目录拷到另一个项目,在settings中安装这个App,在urls.py中include这个App的urls,就OK了。至少里面所有的功能都是正常可用的。(当然,模板文件可能需要针对新的项目做些改动,但是如果你的模板设计规则是相同的,那么只要在新项目的framework里使用相同的内容,而使用另一种不同的style文件就可以了。)

花了不少时间把理财易的代码整理了一遍,整理了一下功能的组织,把用户部分/留言板/日记本/投票以及站内信/好友/圈子(这三个功能在理财易中没有开放)全部建成单独的App,并分离了Urls和模板,虽然导致项目目录下面的子目录多了很多,但是每个目录的功能都简单明白了很多,可以很方便的添加或者移除某个App,比如圈子或者站内信,只要在配置文件中安装该App,相应的页面上添加链接,该功能就变得马上可用了。

最基本的理论就是这样,剩下还有一些高级技巧,比如:

  1. 每个App下面都可以建一个叫sql的目录,里面建立对应于model名称的sql文件,那么,在执行了syncdb命令安装该App以后,这个SQL文件就会被自动调用,可以用来往分类表里插入系统默认的分类,或者往用户表里插入一条最高权限的初始用户等等。
  2. 每个App下面都可以建一个叫templatetags的目录,在里面添加template tags和filters,大家互不干扰。
  3. 每个App下面都可以建一个叫tests的子目录,里面放一些单元测试的代码,就可以直接对该App进行单元测试。
  4. 每个App下面都可以建一个叫management.py的文件,里面可以放任意的Python函数,并给该函数添加事件接口声明,比如dispatcher.connect(my_syncdb_func, signal=signals.post_syncdb),这样就可以在App安装完成的时候执行任意的功能。(注意,是在添加了任何一个App以后都会被调用到,而不止是自己被安装以后。)
  5. 还有,每个App下面的views.py不是必须的,你可以按照自己的需求把函数拆解到多个python文件中,只要在urls.py中引用了正确的类名就可以了。这对于一个功能比较多的App是相当有用的。

有了这些属性的帮忙,基本上你可以对自己的项目文件做出各种适合自己的调整,并且仍然保持项目结构的优雅,并且,对于程序的执行效能是完全没有影响的。

希望这篇文章对于Django的用户起到一点帮助。