Skip to main content

A Django buildout recipe

Project description

Warning

This recipe has been deprecated in favor of djc.recipe2. If you are starting a new project, don’t use this package, use djc.recipe2. This package might see minor bug fixes to keep existing buildouts running, but it won’t gain new features.

This recipe allows you to setup a Django project through zc.buildout.

Usage

The main scope of the recipe is to abstract out the settings.py file, allowing settings to reside inside the buildout instead of having them reside into code (leading to an awkard handling of the same in respect to versioning, for example). The settings.py file is generated by a template, either the default one, the default one and a user extension, or a totally new one.

The template uses the Tempita templating system.

The most basic usage of this recipe is as follows:

[buildout]
parts = django

[django]
recipe = djc.recipe
project = my.project

Where my.project is an importable package containing a urls module and a templates directory.

As you see, very few options are specified here: the defaults are used to build up the settings.py file.

Of course, real examples tend to be slightly more complex: see Options, Default template options and Example usage for more details.

Running tests

The README.txt located within the package also acts as main doctest.

To run the tests, check out the source, and then bootstrap and run the buildout:

$ python bootstrap.py
$ bin/buildout

Then you can run the tests using:

$ bin/test

Detailed Documentation

Options

The options of this recipe are not fixed, as many of those are used exclusively within the settings template file (see Templating).

Here we present the options that have an impact on the recipe aswell:

project
This identifies a python module (in dotted notation) that can serve as project package. The bare minimum for a project package is to contain a urls.py file and a templates directory. It is mandatory unless both the urlconf and templates option are defined.
urlconf
Identifies the module that contains the url definition: if omitted the file urls.py inside the module given as project is used.
templates
Identifies the templates directory. If omitted, the directory named templates located in the module given as project is used.
static-directory
Identifies the folder into which static content (images, CSS and Javascripts) will go. Relatives path are considered relative to the buildout directory. The directory will be created if not present, and nothing will be done if it already exists. If omitted, defaults to static.
media-directory
Identifies the folder into which uploaded files will go. If omitted, defaults to media.
settings-template
If specified, the given template is used to generate the settings.py file, if not provided, the default template will be used. See Templating for more details.
settings-template-extension
If specified, the given template is appended to the template specified by settings template or to the default one.
static-origin
If specified, defines directories from which to copy the static files that have to go in static-directory: see Static origin for more details.
link-static-origin
Boolean value, defaults to false. If set, the files will be symlinked instead of copied. Does work only on unix.
media-origin
If specified, defines directories from which to copy the data files that have to go in media-directory: see static-origin option for details.
base-settings
A settings module (only absolute imports) that is extended by the current settings, for example my.module.settings. If specified, the defaults for apps, middleware and template-loaders becomes an empty string (resulting into them not being written at all). It is unset by default.
wsgi
Defaults to false. If set to true (or on or 1) creates a script in parts/$partname named $partname.wsgi.py that can be used as WSGI script in Apache or other WSGI enabled webserver.
wsgi-logfile
If set, the log will be redirected here: defaults to not being set.
wsgi-loglevel
Sets the log level: it is only processed if wsgi-loglevel is also set. The accepted values are: debug, info, warning, error, critical
coding
The encoding of the resulting settings file. Defaults to utf-8.

Advanced options

Note

All these options are optional and should not be necessary under normal conditions, but might be useful to advanced users.

The following advanced options are supported:

extra-paths
A number of non-standard paths where additional python modules are located.
pth-files
A number of pth-files from which to load additional python modulesthat should be present in the buildout.
initialization
Allows extra python code to be added to both the manage and the WSGI script: see Custom initialization for more details.
environment-vars

Allows one to override OS environment vars by setting them during manage.py run. One environment variable name and value per line, space separated. Variable values can be hardcoded or buildout template mechanism can be used where you refer to other variables set in buildout.

Example:

environment-vars =
        GOOGLE_APPENGINE_PROJECT_ROOT ${buildout:appengine-base-path}
        TZ Europe/Helsinki

You might want to also check gocept.recipe.env buildout recipe if you wish to extend existing environment variables, like PATH. See Custom initialization for more details and an example.

Templating

The settings.py file is generated by interpolating the options of the buildout section with a template, be it the default one or the one provided by the settings-template option.

The template must be a valid Tempita template, to which the whole options of the current buildout section is passed as namespace, integrated as follow:

  1. In the options name, all minuses (-) are converted to underscores (_)
  2. The option name and secret are added, respectively mapping to the buildout section name and to a randomly-generated secret [1].
  3. A serie of functions is added to the namespace to simplify the handling of some situations, see below for more details.

Functions

A certain number of functions can be used inside the templates:

absolute_url
Takes a path and, if it is relative, concatenates it with the buildout location to make it absolute.
listify
Takes a chunk of data, splits it into lines, trims those lines and returns the obtained list, from which void strings are purged.
rfc822tuplize
This function is quite specialized and takes any string in the form Full Name <email.address@example.com> into a tuple composed by the full name and the mail address. It will return a tuple with the unchanged data if the data fed in does not conform to the specifics.
boolify
This functions returns True if the data fed is is any of true, on, 1 (case- insensitive) and False otherwise
join
Equivalent of string’s join() method, with the data to join as first parameter, the infix as second and two optional parameters prefix (added just one to the beginning) and suffix (added just one to the end)
dump
An alias of repr.

Default template options

The default template accepts a number of options. They are to be considered all optional, as sensible defaults will be provided if omitted.

database

The settings of the default Django database, in the form engine=<backend> (user=<user> password=<password>) (host=<host>) (port=<port>) name=<name> (options=(<options>)). Parenthesized values are to be considered as optional. It is to be noted that the options are expected to be enclosed within parentesis ((), separated by comma and in the form <name>=<value>. An example url might be: engine=django.db.backends.mysql user=usr password=pwd host=localhost port=3306 name=mydb options=(opt1=val1,opt2=val2) where usr, pwd, mydb etc should be replaced by your configuration values. Defaults to engine=django.db.backends.sqlite3 name=/${buildout:directory}/storage.db

Note

The old url-like format is still functioning but is currently deprecated and might be removed in the future.

additional-databases
A list of databases in the form name=parameters, each on one line, where name is the Django-internal database name and parameters is the database settings in the same form as that provided by database
media-url
The static content prefix path. Defaults to media
admin-media
The admin only static content prefix path. Defaults to admin_media
timezone
The timezone: defaults to America/Chicago
language-code
The language code: defaults to en-us
use-l10n
Whether to use l10n or not: defaults to true
server-mail
The email address from which site-generate mails come from. Defaults to root@localhost
admins
The list of site admins, in RFC822 form. Defaults to John Smith <root@localhost>
managers
The list of managers: same as for admins. Defaults to copy the value of admins
middleware
The list of middleware classes to load. If an empty string, the value is not written at all.
apps
The list of apps to load. If empty, the value is not written at all.
template-loaders
The list of template loaders to use. If empty, the value is not written at all.
debug
If true, activates debug mode. Defaults to false
internal-ips
The IPs that are allowed to see full stack traces when in debug. Defaults to 127.0.0.1
site-id
The Django site id. Defaults to unset.
template-context-processors
The Django template context processors. Defaults to unset.
authentication-backends
The Django authentication backends. Defaults to unset
languages
A list of supported languages in the form code Fullname, for example en-us English (US). Defaults to unset.
mail-backend
The mail backend to use. Defaults to django.core.mail.backends.smtp.EmailBackend.
mail-filepath
The directory to use if the file mail backend is used [2].
smtp-host
The SMTP host to use when sending mail. Defaults to unset.
smtp-port
The SMTP server port. Defaults to unset.
smtp-user
The username to use to connect to the SMTP server. Defaults to unset.
smtp-password
The password to use to connect to the SMTP server. This is not valid if smtp-user is not set aswell. Defaults to unset.
smtp-tls
Whether TLS should be used when connecting to the SMTP server (boolean option). Defaults to false.
site-domain
The site domain. Defaults to unset.
site-name
The site title. Defaults to unset.
cache-backend
The cache backend. Defaults to locmem:///.
cache-timeout
The cache timeout in seconds. Defaults to 60*5.
cache-prefix
The cache prefix (prefixed at all cache IDs). Defaults to Z.
fixture-dirs
The directories into which search for fixtures. Not set by default.

Deprecated options

These options are still supported within templates, but they are pending deletion.

database-engine
The database engine to use.
database-name
The name of the database to use.
database-user
The username to use when connecting to the database server. Defaults to empty string.
database-password
The password to use when connecting to the database server. Defaults to empty string.
database-host
The host on which the database server resides. Defaults to empty string.
database-port
The port on which the database server accepts connections. Defaults to empty string.

Example usage

As first thing, we need to have a Django project egg around. We have made a very simple one just for testing and we have created a source distribution for it located in packages.

This is of course not the only way you can distribute and obtain the project egg: for example, during developement, it is recommended to use mr.developer for that.

That cleared, we create the most simple buildout conceivable using this recipe

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... """ % cache_dir)

And run it

>>> print "start\n", system(buildout)
start
...
Installing django.
Getting distribution for 'dummydjangoprj'.
...
django: Generating settings in ...
django: Making empty static directory ...
django: Creating script at ...
Generated script ...
...
<BLANKLINE>

This generated some files and directories for us:

  1. A Django manage.py wrapper located at bin/django
  2. A media directory (empty) at static (default option)
  3. A settings file located in parts/django/djc_recipe_django/settings.py

So, as we can see, we have a static directory in the root, a bin/django script and a parts/django part

>>> ls(sample_buildout)
-  .installed.cfg
-  .secret.cfg
d  bin
-  buildout.cfg
d  develop-eggs
d  eggs
d  media
d  packages
d  parts
d  src
d  static
>>> ls('bin')
-  buildout
-  django
>>> ls('parts')
d  buildout
d  django

Let’s look at this first

>>> ls('parts', 'django')
d  djc_recipe_django
>>> ls('parts', 'django', 'djc_recipe_django')
-  __init__.py
-  settings.py

Therefore, we can see how djc_recipe_django is actually an importable python module.

If we examine it:

>>> cat('parts', 'django', 'djc_recipe_django', 'settings.py')
# coding=utf-8
SERVER_EMAIL = 'root@localhost'
ADMINS = (
<BLANKLINE>
    ('John Smith', 'root@localhost'),
)
MANAGERS = ADMINS
<BLANKLINE>
<BLANKLINE>
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/sample-buildout/storage.db'}}
<BLANKLINE>
TIME_ZONE = 'America/Chicago'
<BLANKLINE>
LANGUAGE_CODE = 'en-us'
<BLANKLINE>
USE_L10N = True
<BLANKLINE>
STATIC_ROOT = '.../static'
<BLANKLINE>
STATIC_URL = '/static/'
<BLANKLINE>
MEDIA_ROOT = '.../media'
<BLANKLINE>
MEDIA_URL = '/media/'
<BLANKLINE>
ADMIN_MEDIA_PREFIX = '/admin_media/'
<BLANKLINE>
SECRET_KEY = '...'
<BLANKLINE>
ROOT_URLCONF = 'dummydjangoprj.urls'
<BLANKLINE>
<BLANKLINE>
TEMPLATE_DIRS = (
    '.../dummydjangoprj/templates',
)
<BLANKLINE>
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
<BLANKLINE>
CACHE_BACKEND = 'locmem:///'
CACHE_TIMEOUT = 60*5
CACHE_PREFIX = 'Z'
<BLANKLINE>
DEBUG = False
TEMPLATE_DEBUG = DEBUG

As you can see, this is pretty much the standard Django settings.py as created by Django’s django-admin. It has the peculiarity of not residing in a module, however, but is loaded at run time into the appropriate manage script as a ghost module named _django_settings.

Let’s have a look at the manage script

>>> cat('bin', 'django')
#!...
<BLANKLINE>
import sys
sys.path[0:0] = [
  ...
  ]
<BLANKLINE>
import djc.recipe.manage
<BLANKLINE>
if __name__ == '__main__':
    djc.recipe.manage.main('djc_recipe_django.settings')

As we can see, the main() function of the manage module is called, passing in the file with the settings as only argument.

We can now try to set up an example development environment, passing debug = true to it:

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... debug = true
... """ % cache_dir)
>>> print "start\n", system(buildout)
start
...
Installing django.
django: Making ... a module
django: Generating settings in ...
django: Making empty static directory ...
django: Creating script at ...
Generated script ...
<BLANKLINE>

And look at the generated settings:

>>> cat('parts', 'django', 'djc_recipe_django', 'settings.py')
# coding=utf-8
SERVER_EMAIL = 'root@localhost'
ADMINS = (
<BLANKLINE>
    ('John Smith', 'root@localhost'),
)
MANAGERS = ADMINS
<BLANKLINE>
<BLANKLINE>
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/sample-buildout/storage.db'}}
<BLANKLINE>
TIME_ZONE = 'America/Chicago'
<BLANKLINE>
LANGUAGE_CODE = 'en-us'
<BLANKLINE>
USE_L10N = True
<BLANKLINE>
STATIC_ROOT = '.../static'
<BLANKLINE>
STATIC_URL = '/static/'
<BLANKLINE>
MEDIA_ROOT = '.../media'
<BLANKLINE>
MEDIA_URL = '/media/'
<BLANKLINE>
ADMIN_MEDIA_PREFIX = '/admin_media/'
<BLANKLINE>
SECRET_KEY = '...'
<BLANKLINE>
ROOT_URLCONF = 'dummydjangoprj.urls'
<BLANKLINE>
<BLANKLINE>
TEMPLATE_DIRS = (
    '.../dummydjangoprj/templates',
)
<BLANKLINE>
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
<BLANKLINE>
CACHE_BACKEND = 'locmem:///'
CACHE_TIMEOUT = 60*5
CACHE_PREFIX = 'Z'
<BLANKLINE>
DEBUG = True
TEMPLATE_DEBUG = DEBUG
<BLANKLINE>
INTERNAL_IPS = (
    '127.0.0.1',
)

Template overriding

As it was said in Templating, the default template can be overridden or extended.

Let’s start by extending it:

>>> write('template-extension.py.in',
... """
... # Here we can extend the template, using variables pulled in from the
... # buildout section, with the dashes converted to underscores
... MY_CONFIG_VARIABLE = '{{config_variable_one}}'
... """)
>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... settings-template-extension = template-extension.py.in
... config-variable-one = test
... """ % cache_dir)

Launch the buildout and then take a look at the generated settings.py file

>>> print system(buildout)
Uninstalling django.
Installing django.
...
Generated script ...
<BLANKLINE>
>>> cat('parts', 'django', 'djc_recipe_django', 'settings.py')
# coding=utf-8
SERVER_EMAIL = 'root@localhost'
ADMINS = (
<BLANKLINE>
    ('John Smith', 'root@localhost'),
)
MANAGERS = ADMINS
<BLANKLINE>
<BLANKLINE>
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/sample-buildout/storage.db'}}
<BLANKLINE>
TIME_ZONE = 'America/Chicago'
<BLANKLINE>
LANGUAGE_CODE = 'en-us'
<BLANKLINE>
USE_L10N = True
<BLANKLINE>
STATIC_ROOT = '.../static'
<BLANKLINE>
STATIC_URL = '/static/'
<BLANKLINE>
MEDIA_ROOT = '.../media'
<BLANKLINE>
MEDIA_URL = '/media/'
<BLANKLINE>
ADMIN_MEDIA_PREFIX = '/admin_media/'
<BLANKLINE>
SECRET_KEY = '...'
<BLANKLINE>
ROOT_URLCONF = 'dummydjangoprj.urls'
<BLANKLINE>
<BLANKLINE>
TEMPLATE_DIRS = (
    '.../dummydjangoprj/templates',
)
<BLANKLINE>
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
<BLANKLINE>
CACHE_BACKEND = 'locmem:///'
CACHE_TIMEOUT = 60*5
CACHE_PREFIX = 'Z'
<BLANKLINE>
DEBUG = False
TEMPLATE_DEBUG = DEBUG
<BLANKLINE>
<BLANKLINE>
# Extension template template-extension.py.in
<BLANKLINE>
<BLANKLINE>
# Here we can extend the template, using variables pulled in from the
# buildout section, with the dashes converted to underscores
MY_CONFIG_VARIABLE = 'test'

As you can see, the aditional template has been simply appended to the default, and the variable config-variable-one has been substituted.

If, instead, we totally override the template:

>>> write('template.py.in',
... """
... # Total override
... FOODS = (
...     {{join(listify(foods), "',\\n    '", "'", "',")}}
... )
... """)
>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... settings-template = template.py.in
... foods =
...     spam
...     spam
...     eggs
...     spam
... """ % cache_dir)

Launch the buildout and then take a look at the generated settings.py file

>>> print system(buildout)
Uninstalling django.
Installing django.
...
Generated script ...
<BLANKLINE>
>>> cat('parts', 'django', 'djc_recipe_django', 'settings.py')
# Total override
FOODS = (
    'spam',
    'spam',
    'eggs',
    'spam',
)

As you can see, the builtin template has been totally discarded.

Static origin

Static files are generally not served through Django, but instead the front-end web server takes care to serve them by exposing a directory on the filesystem to the web.

However, many static files (think .js or .css) are part of the functionality of a project or application, and would be interesting to be able to distribute them alongside the code.

Note

The method here described works only for applications and packages that are not installed as zipped modules: for example the egg default format is a zipped file that does not get extracted after installation unless a proper option is passed to easy_install

The relevant resources can be included in the distributed package and use of the static-origin option will allow them to be copied into the static-directory folder (see Options).

A similar feature is present for media files (e.g. image uploads) as well (option media-origin, which ends up into media-directory).

static-origin can contain a list of static file sources, and each item of the list can be either in the form package:directory or package:directory:destination; package being the full dotted name of the importable module, directory the path to the directory inside the module containing static data, and destination an optional subdirectory inside static-directory where to copy the files.

Let’s then begin from the first, simple case, with a single source of static data.

The source of static data is the package dummydjangoapp1, residing as a developement package inside src.

>>> ls('src', 'dummydjangoapp1', 'dummydjangoapp1', 'static')
-  lib1.js
-  main.css
>>> cat('src', 'dummydjangoapp1', 'dummydjangoapp1', 'static', 'main.css')
body { font-family: "Helvetica" "Arial" sans-serif; }

Let’s create a buildout config and run it

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
... develop = src/dummydjangoapp1
... eggs = dummydjangoapp1
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... static-directory = static
... static-origin = dummydjangoapp1:static
... """ % cache_dir)
>>> rmdir('static')
>>> print system(buildout)
Develop: '.../dummydjangoapp1'
Uninstalling django.
Installing django.
...
django: Making static directory '.../static'
...
Generated script ...
<BLANKLINE>

And now let’s see what’s in static

>>> ls('static')
-  lib1.js
-  main.css
>>> cat('static', 'main.css')
body { font-family: "Helvetica" "Arial" sans-serif; }

Let’s now try using two sources: the second is another dummy app, named dummydjangoapp2, that like the first one resides in src.

Let’s see what’s in its static for us:

>>> ls('src', 'dummydjangoapp2', 'dummydjangoapp2', 'static')
-  lib2.js
-  main.css

It seems this app too defines a main.css, so let’s look at the content:

>>> cat('src', 'dummydjangoapp2', 'dummydjangoapp2', 'static', 'main.css')
h1 { color: #92B8D8; }

But this poses a problem! What happens when I put this as second source, and both define main.css? Well, the intuitive thing to do here is probably to override the file, so that the source at the bottom is the top skin layer.

So if we have this buildout

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
... develop =
...     src/dummydjangoapp1
...     src/dummydjangoapp2
... eggs =
...     dummydjangoapp1
...     dummydjangoapp2
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... static-directory = static
... static-origin =
...     dummydjangoapp1:static
...     dummydjangoapp2:static
... """ % cache_dir)

It is reasonable to expect that, after running it, the content of the main.css file is the one provided by the version held by dummydjangoapp2 rather than the one held by dummydjangoapp2.

A quick run and inspect confirms this:

>>> rmdir('static')
>>> print system(buildout)
Develop: '.../dummydjangoapp1'
Develop: '.../dummydjangoapp2'
Uninstalling django.
Installing django.
...
django: Making static directory '.../static'
...
Generated script ...
<BLANKLINE>
>>> ls('static')
-  lib1.js
-  lib2.js
-  main.css
>>> cat('static', 'main.css')
h1 { color: #92B8D8; }

However, I might not want the main.css override to happen, or any other clash between applications, for that matter. That is easily solved by a buildout written like this

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
... develop =
...     src/dummydjangoapp1
...     src/dummydjangoapp2
... eggs =
...     dummydjangoapp1
...     dummydjangoapp2
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... static-directory = static
... static-origin =
...     dummydjangoapp1:static:app1
...     dummydjangoapp2:static:app2
... """ % cache_dir)

It is to be noticed that the static-origin values have now three elements, the latter being the destination directory, which is defined as a subdirectory of static: in this case, both apps live in their subdirectory and no clash happens

>>> rmdir('static')
>>> print system(buildout)
Develop: '.../dummydjangoapp1'
Develop: '.../dummydjangoapp2'
Uninstalling django.
Installing django.
...
django: Making static directory '.../static'
...
Generated script ...
<BLANKLINE>
>>> ls('static')
d  app1
d  app2
>>> ls('static', 'app1')
-  lib1.js
-  main.css
>>> cat('static', 'app1', 'main.css')
body { font-family: "Helvetica" "Arial" sans-serif; }
>>> ls('static', 'app2')
-  lib2.js
-  main.css
>>> cat('static', 'app2', 'main.css')
h1 { color: #92B8D8; }

Of course, this behaviour is not usefol only in this case: an application might actually require you to put the static files in a precise subdirectory irrespective of the fact that other apps might be present or a clash occur.

WSGI

The wsgi option will create a small module [3] inside parts, that will allow you to hook your application to an upstream wsgi server.

In order to have the buildout, we must set the wsgi option of the recipe to true:

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... wsgi = true
... """ % cache_dir)

And launch the buildout:

>>> print "start\n", system(buildout)
start
...
Installing django.
...
django: Generating settings in ...
...
django: Creating script at .../bin/django
Generated script '.../bin/django'.
django: Creating script at .../parts/django/djc_recipe_django/app.py
Generated script '.../parts/django/djc_recipe_django/app.py'.
<BLANKLINE>

The script will then create inside parts/<part_name>/djc_recipe_<part_name> a python module containing an app.py file, which can be loaded by Apache or uwsgi:

>>> ls('parts', 'django', 'djc_recipe_django')
-  __init__.py
-  app.py
-  settings.py
>>> cat('parts', 'django', 'djc_recipe_django', 'app.py')
#!...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
  ...
  ]
<BLANKLINE>
import djc.recipe.wsgi
<BLANKLINE>
application = djc.recipe.wsgi.main('djc_recipe_django.settings')
<BLANKLINE>
def app_factory(global_config, **local_config):
    """This function wraps our simple WSGI app so it
    can be used with paste.deploy"""
    return application

This will take care to inject all the needed paths into sys.path, so no further meddling should be needed.

Most WSGI servers do handle logging effectively by themselves, however if this was not the case, an option to have a separate log output can be used: wsgi-logfile, if set, will cause all the applicative log output to be written to the specified file.

Let’s write the buildout

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... wsgi = true
... wsgi-logfile = wsgi.log
... """ % cache_dir)

Launch it

>>> print "start\n", system(buildout)
start
...
Installing django.
...
django: Generating settings in ...
...
django: Creating script at .../bin/django
Generated script '.../bin/django'.
django: Creating script at .../parts/django/djc_recipe_django/app.py
Generated script '.../parts/django/djc_recipe_django/app.py'.
<BLANKLINE>

And check what changes

>>> cat('parts', 'django', 'djc_recipe_django', 'app.py')
#!...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
  ...
  ]
<BLANKLINE>
import djc.recipe.wsgi
<BLANKLINE>
application = djc.recipe.wsgi.main(..., logfile = '.../wsgi.log')
<BLANKLINE>
def app_factory(global_config, **local_config):
    """This function wraps our simple WSGI app so it
    can be used with paste.deploy"""
    return application

As you can see, the log file parameter is passed to the application: it is to be noted that all relative paths are intended as relative to the buildout root.

Custom initialization

Sometimes we have the need to add some particular initialization code to both the manage script and the WSGI application, or have certain environment variables set in that process without recurring to esoteric configuration.

The first need is resolved by the initialization option: suppose we want our manage and WSGI scripts to check that an integer is really an integer before starting (hence safely aborting if the world has turned upside down).

We would write our buildout:

>>> write('buildout.cfg', #doctest:-NORMALIZE_WHITESPACE
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... wsgi = true
... initialization =
...     >>> if not isinstance(1, int):
...     ...     raise TypeError("World has turned upside down")
... """ % cache_dir)

And launch it:

>>> print "start\n", system(buildout)
start
...
Installing django.
...
django: Generating settings in ...
...
django: Creating script at .../bin/django
Generated script '.../bin/django'.
django: Creating script at .../parts/django/djc_recipe_django/app.py
Generated script '.../parts/django/djc_recipe_django/app.py'.
<BLANKLINE>

And see that our code is present in both bin/django and app.py:

>>> cat('bin', 'django') #doctest:-NORMALIZE_WHITESPACE
#!...
<BLANKLINE>
import sys
sys.path[0:0] = [
    ...
    ]
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
if not isinstance(1, int):
    raise TypeError("World has turned upside down")
<BLANKLINE>
<BLANKLINE>
import djc.recipe.manage
<BLANKLINE>
if __name__ == '__main__':
    djc.recipe.manage.main('djc_recipe_django.settings')
>>> cat('parts', 'django', 'djc_recipe_django', 'app.py') #doctest:-NORMALIZE_WHITESPACE
#!...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
    ...
    ]
<BLANKLINE>
<BLANKLINE>
if not isinstance(1, int):
    raise TypeError("World has turned upside down")
<BLANKLINE>
<BLANKLINE>
import djc.recipe.wsgi
<BLANKLINE>
application = djc.recipe.wsgi.main('djc_recipe_django.settings')
<BLANKLINE>
def app_factory(global_config, **local_config):
    """This function wraps our simple WSGI app so it
    can be used with paste.deploy"""
    return application

It is important to note that the first line of the python code was prepended with >>> while all the subsequent lines were prepended with ... (plus a space, on both). This syntax is necessary if you want to preserve indentation: if you don’t want, you can omit them but you must make sure to never have constructs.

A slightly more useful example would be the need to have special environment variables set before django is initialized, for example one might want to set GOOGLE_APPENGINE_PROJECT_ROOT to /my/path.

In order to do so, the environment-vars option is used:::

>>> write('buildout.cfg',
... """
... [buildout]
... parts = django
... offline = false
... download-cache = %s
... newest = false
... index = http://pypi.python.org/simple/
... find-links = packages
...
... [django]
... recipe = djc.recipe
... project = dummydjangoprj
... wsgi = true
... environment-vars =
...     GOOGLE_APPENGINE_PROJECT_ROOT /my/path
... """ % cache_dir)

The buildout is launched:

>>> print "start\n", system(buildout)
start
...
Installing django.
...
django: Generating settings in ...
...
django: Creating script at .../bin/django
Generated script '.../bin/django'.
django: Creating script at .../parts/django/djc_recipe_django/app.py
Generated script '.../parts/django/djc_recipe_django/app.py'.
<BLANKLINE>

And see that environment variables initialization code is present (via os.environ) in both bin/django and app.py:

>>> cat('bin', 'django')
#!...
<BLANKLINE>
import sys
sys.path[0:0] = [
  ...
  ]
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
import os
os.environ["GOOGLE_APPENGINE_PROJECT_ROOT"] = r"/my/path"
<BLANKLINE>
<BLANKLINE>
import djc.recipe.manage
<BLANKLINE>
if __name__ == '__main__':
    djc.recipe.manage.main('djc_recipe_django.settings')
>>> cat('parts', 'django', 'djc_recipe_django', 'app.py')
#!...
<BLANKLINE>
<BLANKLINE>
import sys
sys.path[0:0] = [
  ...
  ]
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
import os
os.environ["GOOGLE_APPENGINE_PROJECT_ROOT"] = r"/my/path"
<BLANKLINE>
<BLANKLINE>
import djc.recipe.wsgi
<BLANKLINE>
application = djc.recipe.wsgi.main('djc_recipe_django.settings')
<BLANKLINE>
def app_factory(global_config, **local_config):
    """This function wraps our simple WSGI app so it
    can be used with paste.deploy"""
    return application
[1]In all truth, it tries to read it from .secret.txt: that failing the secret code is generated and written to said file to be used subsequently.
[2]For further information, refer to Django’s docs at http://docs.djangoproject.com/en/1.3/ref/settings/#email-file-path
[3]The small module is needed because uwsgi will refuse to load a rogue script, but will load a module (hence, with some PYTHONPATH magic, all comes along)

Contributors

Inital developement sponsored by Abstract Open Solutions

Change history

0.9.7 (2012-07-02)

  • Made SETTINGS_NAME configurable [Simone Deponti]
  • Made several things more pythonic [Simone Deponti]
  • Deprecated the version [Simone Deponti]

0.9.6 (2012-02-10)

  • Added use-l10n option to default template [Simone Deponti]

0.9.5 (2012-01-12)

  • Fixed media directory deletion bug [Simone Deponti]

0.9.4 (2012-01-03)

  • Fixed another bug with the copier when removing linked trees [Simone Deponti]

0.9.3 (2012-01-02)

  • Fixed bug with static directory copying [Simone Deponti]

0.9.2 (2011-12-23)

  • Added proper “smart” symlinking of static and origin [Simone Deponti]

0.9.1 (2011-12-13)

  • Fixed reST annoyance [Simone Deponti]

0.9 (2011-12-13)

  • Added indentation preservation for initialization code. [Simone Deponti]
  • Added a new default way to express databases [Simone Deponti]
  • Added the ability to reference other sections. [Simone Deponti]
  • Made ‘settings.py’ importable. [Simone Deponti]
  • Sped up tests. [Simone Deponti]

0.8.1 (2011-09-22)

  • Fixed MANIFEST.in [Simone Deponti]

0.8 (2011-09-22)

  • Refactored environment variables support [Simone Deponti]
  • Added initialization support [Simone Deponti]
  • Fixed documentation and added tests [Simone Deponti]

0.7.4 (2011-09-15)

  • Changed repository location [Simone Deponti]
  • Made DEBUG explicit [Simone Deponti]
  • Fixed paths on Windows [Dimitri Roche]

0.7.3 (2011-03-30)

  • Added environment-variables support [Mikko Ohtamaa]

0.7.2 (2010-11-18)

  • Fixed logging and added loglevel [Simone Deponti]

0.7.1 (2010-09-23)

  • Fixed multiple link bug [Simone Deponti]

0.7 (2010-09-23)

  • Added ability to symlink the static origin [Simone Deponti]
  • Refactored working set computation to achieve better performances [Simone Deponti]

0.6.1 (2010-07-22)

  • Fixed encoding bug. [Simone Deponti]

0.6 (2010-07-20)

  • Added new mail settings, restructured defaults. [Simone Deponti]
  • Added support for multiple databases and new-style database settings. [Simone Deponti]
  • Fixed bugs in the tests and documentation. [Simone Deponti]

0.5.1 (2010-06-07)

  • Made the wsgi module more paster-compatible

0.5 (2010-06-03)

Note

This release is potentially backwards-incompatible: media-url and media-directory are now named static-url and static-directory respectively.

  • Fixed MEDIA_URL mess [Simone Deponti]
  • Added INTERNAL_IPS support [Simone Deponti]
  • No defaults for middleware, template loaders and apps [Simone Deponti]

0.3.2 (2010-06-01)

  • Fixed WSGI support (uwsgi, custom loggers) [Simone Deponti]
  • Fixed import bugs [Simone Deponti]

0.3.1 (2010-05-19)

aka “Never release between midnight and 6am”:

  • Fixed absolute path bug for fixture dirs in default template [Simone Deponti]
  • Fixed small template extension bug [Simone Deponti]

0.3 (2010-05-19)

  • Added multiple media-origin support [Simone Deponti]
  • Added fixture-dirs support [Simone Deponti]

0.2 (2010-05-17)

  • First public release [Simone Deponti]

0.1 (2010-04-22)

  • Created package [Simone Deponti]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for djc.recipe, version 0.9.7
Filename, size File type Python version Upload date Hashes
Filename, size djc.recipe-0.9.7.zip (63.4 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page