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_config 的 python 模块(创建一个空的 __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.db 的 SQLite 文件中,但目前这个文件还不存在
>>> 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,你可以用它例如覆盖生产环境中的 DATABASES、LOGGING 和 CACHES 设置,而无需创建一个新的 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,没有任何后缀或前缀
这种选择的原因如下
脚本名为django,无论如何调用buildout部分,都简化了开发过程(在运行buildout后总是bin/django runserver,而且不必去查看在那个特定的buildout中它的名称是什么)
由于在生产中,你只需配置你的WSGI服务器以使用多个进程,因此几乎没有理由在buildout中拥有多个Django部分
但是,如果你确实需要多个部分,默认行为将使一个部分覆盖另一个的脚本。这时你需要使用manage-py-file选项,它允许你为管理脚本提供不同的名称(例如,django1和django2)
首先,我们将我们的示例项目的设置复制到两个不同的位置,分别是myproject1和myproject2
>>> copytree(['sites', 'myproject_site_config'], ... ['sites', 'myproject1_site_config']) >>> copytree(['sites', 'myproject_site_config'], ... ['sites', 'myproject2_site_config'])
然后,我们编写一个有两个部分(myproject1和myproject2)的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魔法
这种定制可以通过两种方式完成
通过在settings.py中执行这些操作
通过修改管理脚本(以及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。
因为
我变老了,我倾向于忘记这一点
有时不是media,而是var/upload/mediafiles或其他什么(是的,我们程序员倾向于在最不合适的方式中表达创造力)
这就是为什么我添加了两个选项,虽然默认情况下它们是关闭的,但如果你必须在我的buildout上工作,我希望你至少打开其中一个。
这些选项是media-directory和static-directory,它们的值分别是媒体根目录和静态根目录的路径。当它们被设置后,如果它们不存在,buildout将创建它们,并将适当的MEDIA_ROOT和STATIC_ROOT设置追加到设置模块。
让我们看看它们是如何工作的。首先,我们检查我们没有任何static或media目录
>>> 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-directory对MEDIA_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
贡献者
Simone Deponti <simone.deponti@abstract.it>,初始作者
Bruno Ripa <bruno.ripa@abstract.it>
Mikko Ohtamaa (@moo9000)
Dimitri Roche
初始开发由Abstract Open Solutions赞助
变更历史
2.1 (2012-07-02)
修复setuptools-git问题。
2.0 (2012-07-02)
重写自djc.recipe [Simone Deponti]
项目详情
djc.recipe2-2.1.zip的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | c1eaab1383284bb55c230a841765ccd3450343cbc31c7146178038066c1e18c0 |
|
MD5 | b1aa7bb9fcaa5b1a436784d1e79485a9 |
|
BLAKE2b-256 | 0303c722cda11ad0338f379ebd81fc252a283e4d91060796ea5350cf74cc0ffa |