对于Python的Django框架部署的一些建议


Posted in Python onApril 09, 2015

“Django应用、配置文件以及其他各种相关目录的最佳布局是什么样的?”

总是有朋友问我们这个问题,因此我想花一点时间,写一下我们究竟是如何看待这个问题的,这样我们就可以很容易让其他人参照这个文档。请注意,这里是基于 Django 1.7.1 版写的,但是可以很容易应用在 Django 1.4 版之后任何版本。

虽然 Django 1.4 发布时,它包含了一个改进后的项目布局(这还用了很长一段时间),但本文有一些优化项目布局的更好建议。
为什么这种布局比较好

我们在这里推荐的项目布局有几个优点,即:

  1.     让你获得、重新打包并复用单个的Django应用来用于其他的项目。这通常是不明确的,正如你正在构建一个不管是否要复用的应用。在一开始以想要复用的方式构建应用,会让这一切变得更加简单。
  2.     鼓励设计可复用的应用。
  3.     环境的详细设置。在一个单一的整体配置文件中,if DEBUG==True 没有什么意义。这使得很容易能看到哪些配置是共享的,哪些是在每个环境的基础上可覆写的。
  4.     环境的具体安装要求(PIP requirements)。
  5.     如果有必要,项目级的模板和静态文件可以覆写应用级的默认值。
  6.     小而更具体的测试文件更易于阅读和理解。

假设你有两个应用 blog 和 users,以及两个开发环境 dev 和 prod。你的项目布局结构应该是这样的:
 

myproject/

    manage.py

    myproject/

        __init__.py

        urls.py

        wsgi.py

        settings/

            __init__.py

            base.py

            dev.py

            prod.py

    blog/

        __init__.py

        models.py

        managers.py

        views.py

        urls.py

        templates/

            blog/

                base.html

                list.html

                detail.html

        static/

           …

        tests/

            __init__.py

            test_models.py

            test_managers.py

            test_views.py

    users/

        __init__.py

        models.py

        views.py

        urls.py

        templates/

            users/

                base.html

                list.html

                detail.html

        static/

            …

        tests/

            __init__.py

            test_models.py

            test_views.py

     static/

         css/

             …

         js/

             …

     templates/

         base.html

         index.html

     requirements/

         base.txt

         dev.txt

         test.txt

         prod.txt

本文的剩余部分介绍了如何将项目迁移到这个布局,以及为什么这种布局比较好。
当前默认布局

我们将调用示例项目foo,我知道这是一个非常有创意的名字。我们假设在这里,我们将要启动foo.com。但当我们希望将我们的项目名称映射最终域名时,该项目将以不以任何意义要求的方式存在在这里。

如果你使用 django-admin.py startproject foo 命令开启这个项目,你会得到一个像这样的目录结构:
 

foo/
  manage.py
  foo/
    __init__.py
    settings.py
    urls.py
    wsgi.py

这种布局是一个好起点,我们有一个顶级目录foo,里面包含了manage.py文件和项目目录foo/foo/。在这个目录,你可以查询到源代码控制系统(比如 Git) 。

你应该想到子目录foo/foo/就是这个项目。这里的所有文件,不是一个Django应用程序,就是与项目相关的配套文件。
修改配置

这里的任务是修正不好的配置文件。我将这个布局向新用户展示,我往往惊讶于这几个人怎么知道这甚至可能做到。事实上,当大家都知道这些配置只是Python代码时,他们也不将它们当做Python代码。

因此,让我们来改进配置。对于oo项目而言,将有4个开发环境:dev、stage、jenkins 和 production。给每个开发环境一个它们自己的配置文件。这个过程中要做的事情是:

    在foo/foo/目录下新建一个配置目录,并在里面创建一个空的__init __.py文件。
    将foo/foo/settings.py移动并重命名为foo/foo/settings/base.py。
    在foo/foo/settings/目录下创建单独的dev.py、stage.py、jenkins.py 和 production.py文件。这四种环境的特定配置文件应该包含如下内容:
   

from base import *

为什么这很重要呢?对于本地开发你想要设置DEBUG=True,但很容易不小心将这个推到生产代码中,因此需要打开 foo/foo/settings/production.py 文件,在初始导入base后加上DEBUG=False。现在,对于这种愚蠢的错误,你的生产环境是安全的。

还有什么可以定制?很明显你可以针对不同的数据库,甚至是不同的主机来配置staging、jenkins和production等开发环境。然后在每个环境配置文件中来调整那些配置。
使用这些配置

无论你通常使用哪种方法,使用这些配置都非常简单。要使用该操作系统的环境,你只要做:
 

export DJANGO_SETTINGS_MODULE=“foo.settings.jenkins”

现在你就在使用 jenkins 的配置。

或者,也许你更喜欢把它们作为这样的命令行选项:
 

./manage.py migrate —settings=foo.settings.production

同样的,如果你使用 gunicorn,命令则如下:
 

gunicorn -w 4 -b 127.0.0.1:8001 —settings=foo.settings.dev

还有什么可自定义的配置?

另一个实用建议是将一些默认的集合配置从元组改为列表。例如 INSTALLED_APPS,将它从:
 

INSTALLED_APPS =(
...
)

改为:
 

INSTALLED_APPS = [
  …
]

现在,基于每个环境的特定配置文件,我们可以更轻松地在 foo/settings/base.py文件中添加和删除应用。例如,你可能只想在dev环境而不是其他环境中安装Django调试工具栏。

这个技巧对 TEMPLATE_DIRS和MIDDLEWARE_CLASSES 配置也非常有用。

我们经常使用的另一个技巧是把应用分为两个列表,一个是项目的必要前提,另一个用于实际项目应用。如下面所示:
 

PREREQ_APPS = [
  ‘django.contrib.auth',
  ‘django.contrib.contenttypes',
  …
  ‘debug_toolbar',
  ‘imagekit',
  ‘haystack',
]
 
PROJECT_APPS = [
  ‘homepage',
  ‘users',
  ‘blog',
]
 
INSTALLED_APPS = PREREQ_APPS + PROJECT_APPS

为什么这个有用?第一,它有助于更好地区分Django核心应用、第三方应用及你自己的内部项目的具体应用。对于测试和代码覆盖率等事情,写明你的特定应用的PROJECT_APPS列表往往就派上了用场。你有写一个应用列表,因此你可以轻松自动地确保它们的测试运行,记录的测试覆盖率只包括它们而不包括任何第三方的应用,且无需在两个不同的地方维护这个列表。
修改要求

大多是项目有一个requirements.txt文件,它用如下命令安装:
 

pip install -r requirements.txt

对于简单的小项目这以足够了,但requirements.txt文件有一个鲜为人知的特点是,你可以使用-r参数来包括其他文件。因此,对于所有常见的安装要求,我们可以创建一个base.txt文件;然后,如果我们需要能够运行测试,我们可以创建一个包含如下内容的特定的requirements/test.txt文件:
 

-r base.txt

pytest==2.5.2

coverage==3.7.1

我承认这没有巨大的好处,但它确实有助于区分什么是每个开发环境的要求。同时,对于其性能,它不会安装一堆在实际生产中用不上的东西,来减少生产环境中的pip安装时间。
测试文件

我们为什么要拆分很大的测试文件呢?其中的一个主要原因是,如果你在一个tests.py文件中对每个应用写了足够多的测试,那么这个文件最终将变得非常臃肿。这样的代码可读性很差,并且你不得不在编辑器中花很多时间来滚动浏览代码。

当你和其他开发者一起工作时,小文件也能让你在代码合并时少遇到冲突。小文件是你的朋友。
URLs

对于小型项目,把所有的URL定义放在foo/urls.py文件中,让它们在同一个地方。但是,如果你的目标是代码的清晰和可复用,你最好在每个应用中定义它们的url,再将它们包含在你的主项目中。你不应如下所做:
 

urlpatterns = patterns(‘',
  url(r'^$', HomePageView.as_view(), name=‘home'),
  url(r'^blog/$', BlogList.as_view(), name=‘blog_list'),
  url(r'^blog/(?P<pk>d+)/$', BlogDetail.as_view(), name=‘blog_detail'),
  …
  url(r'^user/list/$', UserList.as_view(), name=‘user_list'),
  url(r'^user/(?P<username>w+)/$', UserDetail.as_view(), name=‘user_detail'),
)

你应该这样做:
 

urlpatterns = patterns(‘',
  url(r'^$', HomePageView.as_view(), name=‘home'),
  url(r'^blog/‘, include(‘blog.urls')),
  url(r'^user/‘, include(‘user.urls')),
)

模板和静态资源

每个应用中都有templates/和static/目录,这让一个应用可以基本上复用到其他的项目中。

对于一个很酷的功能,我们全在一个包中获得应用提供的默认模板和任何相关的静态资源,如特殊的Javascript。

但是,它也让我们可以覆写每个项目主目录foo/templates/下的模板。我们通过增加一个 templates/blog/detail.html 模板覆写默认的 blog/templates/blog/detail.html 模板。
复用Django应用

假设你已经使用这个布局一段时间,有一天你会意识到你的新项目需要一个blog应用,这个从你的foo项目出来的应用将是完美的。所以你复制、粘贴文件……错误!现在你有这个应用的两个副本。假定你还记得,在一个副本中进行Bug修复和新功能增添需要手动地在项目间迁移。

相反,为你的博客创建一个新的目录,并把foo/blog/目录中的内容放入其中。同时,调整现有的foo项目和你的新项目来进行安装。

如果需要的话,它们仍然可以跟踪这两个不同版本的应用,或持续更新,且获得它们不断发展中的所有bug修复和新功能。你仍然可以在每个项目的基础上,根据需求覆写模板和静态资源,所以这样做真的没有任何问题。

Python 相关文章推荐
python用reduce和map把字符串转为数字的方法
Dec 19 Python
python 读写中文json的实例详解
Oct 29 Python
python通过opencv实现批量剪切图片
Nov 13 Python
数据清洗--DataFrame中的空值处理方法
Jul 03 Python
django认证系统实现自定义权限管理的方法
Jul 16 Python
Python实现手写一个类似django的web框架示例
Jul 20 Python
使用Python-OpenCV向图片添加噪声的实现(高斯噪声、椒盐噪声)
May 28 Python
python破解bilibili滑动验证码登录功能
Sep 11 Python
Python爬取阿拉丁统计信息过程图解
May 12 Python
python如何查看安装了的模块
Jun 23 Python
python中round函数保留两位小数的方法
Dec 04 Python
pycharm 配置svn的图文教程(手把手教你)
Jan 15 Python
Python线程中对join方法的运用的教程
Apr 09 #Python
在Python的Flask框架下使用sqlalchemy库的简单教程
Apr 09 #Python
详解Python中的正则表达式的用法
Apr 09 #Python
Python中几种操作字符串的方法的介绍
Apr 09 #Python
详解Python中的__new__()方法的使用
Apr 09 #Python
Python中动态获取对象的属性和方法的教程
Apr 09 #Python
详解Python中的循环语句的用法
Apr 09 #Python
You might like
给php新手谈谈我的学习心得
2007/02/25 PHP
PHP 反射机制实现动态代理的代码
2008/10/22 PHP
PHP多个版本的分析解释
2011/07/21 PHP
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
2013/06/25 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
2014/04/29 PHP
你可能不知道PHP get_meta_tags()函数
2014/05/12 PHP
PHP实现上一篇下一篇的方法实例总结
2016/09/22 PHP
在页面上点击任一链接时触发一个事件的代码
2007/04/07 Javascript
jquery 插件 web2.0分格的分页脚本,可用于ajax无刷新分页
2008/12/25 Javascript
基于jquery的一个浮动框(扩展性比较好 )
2010/08/27 Javascript
Bootstrap中文本框的宽度变窄并且加入一副验证码图片的实现方法
2016/06/23 Javascript
bootstrap下拉菜单使用方法解析
2017/01/13 Javascript
js实现五星评价功能
2017/03/08 Javascript
JS+jQuery实现注册信息的验证功能
2017/09/26 jQuery
JS实现中文汉字按拼音排序的方法
2017/10/09 Javascript
Angular4绑定html内容出现警告的处理方法
2017/11/03 Javascript
基于JavaScript实现幸运抽奖页面
2020/07/05 Javascript
vue 实现强制类型转换 数字类型转为字符串
2019/11/07 Javascript
JavaScript常用进制转换及位运算实例解析
2020/10/14 Javascript
vue-calendar-component 封装多日期选择组件的实例代码
2020/12/04 Vue.js
一起来了解一下JavaScript的预编译(小结)
2021/03/01 Javascript
[02:52]DOTA2新手基础教程 米波
2014/01/21 DOTA
[04:22]DSPL第二期精彩集锦:残血反杀!
2014/12/10 DOTA
跟老齐学Python之Import 模块
2014/10/13 Python
利用Python实现简单的相似图片搜索的教程
2015/04/23 Python
Python中的默认参数实例分析
2018/01/29 Python
python3.6数独问题的解决
2019/01/21 Python
postman模拟访问具有Session的post请求方法
2019/07/15 Python
OnePlus加拿大官网:中国国际化手机品牌
2020/10/13 全球购物
远程调用的原理
2014/07/05 面试题
公司活动策划方案
2014/01/13 职场文书
安全资金保障制度
2014/01/23 职场文书
转让协议书范本
2014/04/15 职场文书
乡镇党员干部四风对照检查材料思想汇报
2014/09/27 职场文书
初中学生操行评语
2014/12/26 职场文书
python实现图片批量压缩
2021/04/24 Python