跳转到主要内容

Django构建配方

项目描述

此配方允许您通过zc.buildout设置Django项目。

用法

要使用此配方,创建一个buildout如下所示

[buildout]
parts =
    myproject

[myproject]
recipe = djc.recipe2

然后在sites/<part_name>_site_config中创建一个Python模块,包含settings.py文件。

buildout将负责在bin/django创建管理脚本,并在parts/myproject/myproject_part_site/wsgi.py中创建一个用于在生产环境中服务项目的WSGI应用程序。

在我们的示例中,这会产生以下文件结构

<buildout_root>
|
|- bin
|  |
|  |- ...
|  |
|  |- django # the manage.py script
|
|- ...
|
|- parts
|  |
|  |- myproject
|     |
|     |- myproject_part_site # put this on PYTHONPATH when serving via WSGI
|        |
|        |- __init__.py
|        |
|        |- ...
|        |
|        |- wsgi.py # WSGI app and paster app factory
|
|- ...
|
|- sites
|  |
|  |- myproject_site_config
|     |
|     |- __init__.py # void
|     |
|     |- settings.py # your settings here
|
|- ...

有关所有选项和详细文档,请参阅以下内容。

运行测试

包内位于recipe.rst的文件还充当主doctest。

要运行测试,请检出源代码,然后引导和运行buildout

$ python bootstrap.py
$ bin/buildout

如果是全新检出,您还应运行

$ ./makecache.sh

此命令应在检出后仅运行一次:它将下载测试所需的某些包,以便它们可以离线运行。

如果makecache.sh已更改,也应重新运行。

然后您可以使用以下方式运行测试

$ bin/test

详细文档

基本用法

要拥有一个 Django 网站,您必须提供一些配置。

Django 中,配置是通过在 settings 模块 中创建一组全局变量,并让 Django 知道使用哪个配置模块来实现的。

这个食谱在其基本功能上,采用了对配置的 约定优于配置 方法。

因此,您构建中所有 Django 部分的所有配置 必须 放置在您的构建根目录中名为 sites 的目录内。

在这个目录中,必须创建一个名为 <part_name>_site_configpython 模块(创建一个空的 __init__.py!),并在其中放置包含您设置的 settings.py 文件。

例如,如果我们的 Django 部分命名为 myproject(这里我们指的是构建部分的名称),我们将执行以下操作

>>> mkdir('sites')
>>> mkdir('sites', 'myproject_site_config')
>>> write('sites', 'myproject_site_config', '__init__.py',
...       '#\n')
>>> write('sites', 'myproject_site_config', 'settings.py', '''
... SPAM = 'eggs'
... ''')

好的,这个设置文件并不是一个非常好的例子,但作为一个例子,它现在足够了。

现在我们来创建构建并运行它

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>

如您所见,目前该部分只包含食谱,如果我们遵守其约定,它将无需进一步干预即可 直接运行

让我们看看构建做了什么。首先,它在 bin 中创建了一个 django 二进制文件,这是 Django 的 manage.py 的等价物(这意味着您可以像使用 manage.py 一样调用它)

>>> ls('bin')
-  buildout
-  django

构建的下一件事是在 parts/<part_name> 中创建另一个 python 模块。

>>> ls('parts', 'myproject')
d  myproject_part_site
>>> ls('parts', 'myproject', 'myproject_part_site')
-  __init__.py
-  settings.py
-  wsgi.py

另一个 python 模块?

是的,因为与第一个不同,这个模块完全处于构建的控制之下,并且每次运行 bin/buildout 时都会生成(因此,编辑这些文件是一个 非常糟糕的想法,因为您的更改将不会被保留)。

在这个模块中,我们再次有一个 settings.py 文件,还有一个 wsgi.py 文件。我们将在 进入生产环境 中更详细地查看后者:前者是实际将被 Django 加载的设置模块。

那么我们之前定义的设置怎么办?不要担心,因为构建创建的 settings.py 将导入您编写的模块,并将 SECRET_KEY 设置添加到其中

>>> cat('parts', 'myproject', 'myproject_part_site', 'settings.py')
from myproject_site_config.settings import *
<BLANKLINE>
SECRET_KEY = "..."
<BLANKLINE>
<BLANKLINE>

这种(略微复杂的)设置存在,是因为一个选择不当的 SECRET_KEY 可能会成为一个安全问题(对于病理情况来说,这可能是一个非常严重的问题)。

由于选择一个简单的密钥太容易了(可能是因为我们懒得想一个好的密钥),而且很容易忘记在开发和生产环境中更改它,这个食谱为你生成一个长且随机的密钥。

这样你就可以安全地在你的 settings.py 文件中省略 SECRET_KEY,同时也能保证完全安全。

这个密钥只生成一次,并在 bin/buildout 的各个运行中保持不变。这是可能的,因为该食谱首先会检查构建根目录下是否存在 .secret.cfg 文件:如果存在,它会读取它并从中提取密钥(文件内容本身是密钥,后面跟着一个换行符)。如果不存在,它会生成一个新的密钥并将其写入。因此,只要存在 .secret.cfg 文件,该食谱就会在 bin/buildout 的各个运行中使用相同的密钥。

事实的证明是,我们的构建中存在一个 .secret.cfg 文件

>>> isfile('.secret.cfg')
True

完整示例

现在,让我们在我们的设置文件(《myproject_site_config/settings.py》)中添加一些更合理的值

>>> write('sites', 'myproject_site_config', 'settings.py', '''
... DATABASES = {
...     'default': {
...         'ENGINE': 'django.db.backends.sqlite3',
...         'NAME': 'storage.db'
...     }
... }
... TIME_ZONE = 'Europe/Rome'
... ''')

现在,为了让这些设置生效,我们不需要重新运行 buildout,因为生成的文件所做的导入将会获取它们

>>> print system('bin/django diffsettings')
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'storage.db'}}
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.settings'  ###
TIME_ZONE = 'Europe/Rome'

看起来成功了!

我们决定将数据库放入一个名为 storage.dbSQLite 文件中,但目前这个文件还不存在

>>> isfile('storage.db')
False

现在让我们告诉 Django 创建数据库

>>> print system('bin/django syncdb --noinput')
Creating tables ...
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
<BLANKLINE>

然后我们会看到数据库已经创建

>>> isfile('storage.db')
True

调试模式

我们现在可以开始开发了,但迟早我们会意识到我们没有设置 DEBUG = True,这对于你不是 Donald Knuth 来说是基本的要求。

我们可以在 myproject_site_config/settings.py 中直接添加它,但当我们准备上线时,这可能会引起问题,因为当你公开时,你肯定希望关闭 DEBUG 和它的姐妹们。

因此,我们还有另一个选择,如下

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... settings-override =
...     DEBUG = True
...     TEMPLATE_DEBUG = True
... ''')

我们在 settings-override 中放入的任何内容都将附加到 buildout 生成的 settings.py 的末尾(作为字符串处理,因此请注意不会进行正确性检查)。这使得我们可以在不需要创建两个不同的 settings.py 文件(一个用于生产,一个用于开发)的情况下,快速区分生产和开发构建。

如果我们重新运行 buildout 并查看结果,我们会看到我们现在处于调试模式

>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('parts', 'myproject', 'myproject_part_site', 'settings.py')
from myproject_site_config.settings import *
<BLANKLINE>
SECRET_KEY = "..."
<BLANKLINE>
DEBUG = True
TEMPLATE_DEBUG = True
<BLANKLINE>
>>> print system('bin/django diffsettings')
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'storage.db'}}
DEBUG = True
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.settings'  ###
TEMPLATE_DEBUG = True
TIME_ZONE = 'Europe/Rome'

当然,这不仅仅限于 DEBUG,你可以用它例如覆盖生产环境中的 DATABASESLOGGINGCACHES 设置,而无需创建一个新的 settings.py 文件。

进入生产环境

如上所述,如果我们的开发环境与生产环境差异不大(除了使用真实的缓存、更复杂的RDBMS等),则可以使用settings-override来管理

>>> mkdir('var')
>>> mkdir('var', 'log')
>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... settings-override =
...     DATABASES = {
...         'default': {
...             'ENGINE': 'django.db.backends.postgresql_psycopg2',
...             'HOST': 'localhost',
...             'PORT': '5432',
...             'NAME': 'mydb',
...             'USER': 'mydb',
...             'PASSWORD': 'secret'
...         }
...     }
...     CACHES = {
...         'default': {
...             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
...             'LOCATION': '127.0.0.1:11211',
...         }
...     }
...     LOGGING = {
...         'version': 1,
...         'disable_existing_loggers': True,
...         'root': { 'level': 'WARNING', 'handlers': ['logfile'], },
...         'formatters': {
...             'verbose': {
...                 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
...             },
...         },
...         'handlers': {
...             'logfile': {
...                 'level': 'ERROR',
...                 'class': 'logging.handlers.RotatingFileHandler',
...                 'filename': 'var/log/myproject.log',
...                 'maxBytes': 1024,
...                 'backupCount': 3,
...             },
...             'console': {
...                 'level': 'DEBUG',
...                 'class': 'logging.StreamHandler',
...                 'formatter': 'verbose'
...             }
...         },
...         'loggers': {
...             'django.db.backends': {
...                 'level': 'ERROR',
...                 'handlers': ['console'],
...                 'propagate': False,
...             },
...         },
...     }
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> print system('bin/django diffsettings')
CACHES = ...
DATABASES = ...
LOGGING = ...
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.settings'  ###
TIME_ZONE = 'Europe/Rome'

这实际上是一个相当完整(尽管基本)的生产示例,并且可以在buildout中很好地管理

然而,如果我们确实有更复杂的情况,则可能最好使用外部设置

更改二进制名称

正如我们之前所说,生成的二进制文件名总是django,没有任何后缀或前缀

这种选择的原因如下

  1. 脚本名为django,无论如何调用buildout部分,都简化了开发过程(在运行buildout后总是bin/django runserver,而且不必去查看在那个特定的buildout中它的名称是什么)

  2. 由于在生产中,你只需配置你的WSGI服务器以使用多个进程,因此几乎没有理由在buildout中拥有多个Django部分

但是,如果你确实需要多个部分,默认行为将使一个部分覆盖另一个的脚本。这时你需要使用manage-py-file选项,它允许你为管理脚本提供不同的名称(例如,django1django2

首先,我们将我们的示例项目的设置复制到两个不同的位置,分别是myproject1myproject2

>>> copytree(['sites', 'myproject_site_config'],
...          ['sites', 'myproject1_site_config'])
>>> copytree(['sites', 'myproject_site_config'],
...          ['sites', 'myproject2_site_config'])

然后,我们编写一个有两个部分(myproject1myproject2)的buildout,并运行它

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject1
...     myproject2
...
... [myproject1]
... recipe = djc.recipe2
... manage-py-file = django1
...
... [myproject2]
... recipe = djc.recipe2
... manage-py-file = django2
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject1.
...
Installing myproject2.
...
<BLANKLINE>

我们将看到它创建了两个不同的脚本

>>> ls('bin')
-  buildout
-  django1
-  django2

高级用法

自定义初始化

有时,在Django加载所有内容之前,你需要做一些魔法操作,以使用某些功能

例如,Pinax是一个基于Django的非常知名的社会网站框架,它需要你在初始化之前执行某些sys.path魔法

这种定制可以通过两种方式完成

  1. 通过在settings.py中执行这些操作

  2. 通过修改管理脚本(以及WSGI脚本)

第一个选择看起来可能更简单,但实际上它隐藏了比最初可见的更多复杂性。后者更好,但由于脚本是由buildout生成的,我们无法简单地编辑该文件

在我们实际了解如何操作之前,让我们先做一个假设:我们可以将初始化内容分为两大类

第一类是最常见的,即你需要设置一个环境变量:虽然可以通过执行$ MYVAR=value bin/django来实现,但长期来看并不方便

这时就轮到环境变量出场了!

让我们看一个具体的例子:在Django上运行Google App Engine。Google App Engine要求你设置一个GOOGLE_APPENGINE_PROJECT_ROOT环境变量,否则将无法工作

因此,为了添加它,我们将构建脚本写成如下形式,为每个需要设置的环境变量提供一个变量名和值(用空格分隔)列表

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... environment-vars =
...     GOOGLE_APPENGINE_PROJECT_ROOT /my/path
... ''')

运行后,我们可以看到脚本正确地初始化了环境变量

>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#!...
<BLANKLINE>
...
<BLANKLINE>
import os
os.environ["GOOGLE_APPENGINE_PROJECT_ROOT"] = r"/my/path"
<BLANKLINE>
...
<BLANKLINE>
os.environ['DJANGO_SETTINGS_MODULE'] = "myproject_part_site.settings"
if IS_14_PLUS:
    execute_from_command_line(sys.argv)
else:
    utility = ManagementUtility(sys.argv)
    utility.execute()

对于第二种情况,提供了初始化选项:这允许你以类似于doctest的格式编写在Django启动之前需要执行的Python代码。

让我们看看如何确保当1 != 1时,Django根本不会启动

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... initialization =
...     >>> if 1 != 1:
...     ...     raise RuntimeError("I can't run on quantum computers")
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#!...
<BLANKLINE>
...
<BLANKLINE>
if 1 != 1:
    raise RuntimeError("I can't run on quantum computers")
<BLANKLINE>
...
<BLANKLINE>
os.environ['DJANGO_SETTINGS_MODULE'] = "myproject_part_site.settings"
if IS_14_PLUS:
    execute_from_command_line(sys.argv)
else:
    utility = ManagementUtility(sys.argv)
    utility.execute()

媒体和静态文件

这有点个人喜好。当基于别人的工作开发时,我发现上传无法工作非常令人烦恼,因为在检出和运行buildout后,我没有做$ mkdir media

因为

  1. 我变老了,我倾向于忘记这一点

  2. 有时不是media,而是var/upload/mediafiles或其他什么(是的,我们程序员倾向于在最不合适的方式中表达创造力)

这就是为什么我添加了两个选项,虽然默认情况下它们是关闭的,但如果你必须在我的buildout上工作,我希望你至少打开其中一个。

这些选项是media-directorystatic-directory,它们的值分别是媒体根目录和静态根目录的路径。当它们被设置后,如果它们不存在,buildout将创建它们,并将适当的MEDIA_ROOTSTATIC_ROOT设置追加到设置模块。

让我们看看它们是如何工作的。首先,我们检查我们没有任何staticmedia目录

>>> isdir('media')
False
>>> isdir('static')
False

然后编写并运行buildout

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... media-directory = media
... static-directory = static
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>

然后我们看到我们有了目录和设置

>>> isdir('media')
True
>>> isdir('static')
True
>>> print system('bin/django diffsettings')
DATABASES = ...
MEDIA_ROOT = '...'
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.settings'  ###
STATIC_ROOT = '...'
TIME_ZONE = 'Europe/Rome'

显然,你不需要一起使用它们,但它们可以独立使用。

外部设置

有时,一个文件的所有设置还不够,或者可能会发现settings-override对你来说不太方便。

这就是为什么这个配方允许你使用sys.path中的任何内容作为设置模块。

例如,假设我们想在单独的文件中放置我们的生产设置:我们可能会创建一个名为sites/myproject_site_config/production.py的文件,并使用它作为设置模块。

首先,让我们创建这个文件

>>> write('sites', 'myproject_site_config', 'production.py', '''
... from .settings import *
... TIME_ZONE = 'Europe/London'
... ''')

然后我们告诉buildout使用模块myproject_site_config.production作为设置模块,而不是默认模块,通过settings-module选项

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... settings-module = myproject_site_config.production
... ''')

然后我们可以运行buildout并看看发生了什么

>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> print system('bin/django diffsettings')
DATABASES = ...
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.settings'  ###
TIME_ZONE = 'Europe/London'

如你所见,变化生效了。

选项参考

eggs

生成脚本必须访问的egg列表。这通常包括你的应用程序egg及其依赖项,如果后者的依赖关系没有在setup.py文件中明确说明。

它们可以是部分选项

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... eggs = django-gravatar2
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    '.../eggs/django_gravatar2-1.0.4-...egg',
    ...
    ]
<BLANKLINE>
...

或者作为buildout选项

>>> write('buildout.cfg', '''
... [buildout]
... eggs = django-gravatar2
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    '.../eggs/django_gravatar2-1.0.4-...egg',
    ...
    ]
<BLANKLINE>
...

或者两者都可以,它们将被合并

>>> write('buildout.cfg', '''
... [buildout]
... eggs = South
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... eggs = django-gravatar2
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    '.../eggs/django_gravatar2-1.0.4-...egg',
    '.../eggs/South-0.7.5-...egg',
    ...
    ]
<BLANKLINE>
...

环境变量

在执行前需要设置的环境变量列表,每个变量通过换行符分隔,格式为VAR_NAME value

请参阅自定义初始化以获取示例。

额外路径

应通过换行符分隔的路径列表,这些路径需要在代码执行前添加到sys.path中(允许发现自定义模块)。

例如

>>> mkdir('custom_modules')
>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... extra-paths =
...     custom_modules
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    ...
    '.../custom_modules',
    ]
<BLANKLINE>
...

初始化

Python代码,需要按照doctest格式进行格式化,将在任何初始化之前执行。

请参阅自定义初始化以获取示例。

manage.py文件

bin中生成的管理脚本的名称。

请参阅更改二进制名称以获取示例。

设置文件

生成的设置文件的名称(由buildout在每次运行时自动生成的文件)。

此选项非常有用,可以避免模块名称冲突

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... settings-file = configuration.py
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> print system('bin/django diffsettings')
DATABASES = ...
SECRET_KEY = '...'
SETTINGS_MODULE = 'myproject_part_site.configuration'  ###
TIME_ZONE = 'Europe/Rome'

设置模块

加载自定义设置模块而不是传统模块。

请参阅外部设置以获取示例。

设置覆盖

指定一些设置(作为Python代码),并将其附加到自动生成的设置文件中,从而覆盖模块定义的设置。

请参阅调试模式以获取示例。

站点目录

更改传统配置位置(通常是sites目录)的默认位置。

它将被追加到sys.path

>>> copytree(['sites'], ['mysites'])
>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... sites-directory = mysites
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> cat('bin', 'django')
#...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    ...
    '.../mysites',
    ]
<BLANKLINE>
...

静态目录

设置STATIC_ROOT的位置并在缺失时创建它。

请参阅媒体和静态

媒体目录

static-directoryMEDIA_ROOT的作用相同。

wsgi文件

更改包含WSGI应用程序的文件的名称。

其目的类似于settings-file

>>> write('buildout.cfg', '''
... [buildout]
... parts =
...     myproject
...
... [myproject]
... recipe = djc.recipe2
... wsgi-file = wsgiapp.py
... ''')
>>> print "$ bin/buildout\n", system(buildout)
$ bin/buildout
...
Installing myproject.
...
<BLANKLINE>
>>> ls('parts', 'myproject', 'myproject_part_site')
-  __init__.py
-  settings.py
-  wsgiapp.py

贡献者

初始开发由Abstract Open Solutions赞助

变更历史

2.1 (2012-07-02)

  • 修复setuptools-git问题。

2.0 (2012-07-02)

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

djc.recipe2-2.1.zip (38.2 kB 查看哈希值)

上传时间

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面