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

yipeiwu_com5年前Python基础

“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控制台实现tab补全和清屏的例子

在shell(bash)下有2个很基本的功能,那就是tab补全,和clear清屏,对于我这种时不时不自觉的就手残要clear清屏一下的人来说,python控制台不能清屏很不爽,经过goo...

Python中title()方法的使用简介

 title()方法返回所有单词的第一个字符大写的字符串的一个副本。 语法 以下是title()方法的语法: str.title(); 参数  &nb...

python几种常用功能实现代码实例

这篇文章主要介绍了python几种常用功能实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1、python 程序退出的几种...

django创建超级用户过程解析

这篇文章主要介绍了django创建超级用户过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 终端输入 python3 man...

numpy数组做图片拼接的实现(concatenate、vstack、hstack)

两种方法拼接 #img = np.vstack((img, img2)) # vstack按垂直方向,hstack按水平方向 img = np.concatenate((img,...