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.

media-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``.

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.

media-origin
    If specified, defines a directory from which to copy the static files that
    have to go in ``media-directory``: the files will be copied over only if
    ``media-directory`` does not exists. The origin takes the form of
    ``my.module: directory`` where *my.module* is a python module and
    *directory* a directory inside it.

base-settings
    A settings module (only absolute imports) that is extended by the current
    settings.
    If specified, the defaults for ``apps``, ``middleware`` and
    ``template-loaders`` becomes an empty string (resulting into them not being
    written at all).

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.


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.
    

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 [#]_.

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)


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.

media-url
    The static content prefix path. Defaults to ``media``
    
admin-media
    The admin only static content prefix path. Defaults to ``admin_media``
    
database-engine
    The database engine to use: defaults to ``sqlite``
    
database-name
    The name of the database to use: defaults to ``storage.db``
    
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.

timezone
    The timezone: defaults to ``America/Chicago``
    
language-code
    The language code: defaults to ``en-us``
    
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``

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.

smtp-host
    The SMTP host to use when sending mail. Defaults to ``localhost``.

smtp-port
    The SMTP server port. Defaults to 25.

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``.


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
    ... index = http://pypi.python.org/simple/
    ... find-links = packages
    ...
    ... [django]
    ... recipe = djc.recipe
    ... project = dummydjangoprj
    ... """)

And run it ::

    >>> print "start\n", system(buildout) 
    start
    ...
    Installing django.
    django: Specified project 'dummydjangoprj' not found, attempting install
    Getting distribution for 'dummydjangoprj'.
    ...
    django: Generating settings in ...
    django: Making empty media 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/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  packages
    d  parts
    d  static
    >>> ls('bin')
    -  buildout
    -  django
    >>> ls('parts')
    d  django

Let's look at this first ::

    >>> ls('parts', 'django')
    -  settings.py
    >>> cat('parts', 'django', 'settings.py')
    ADMINS = (
    <BLANKLINE>
        ('John Smith', 'root@localhost'),
    )
    MANAGERS = ADMINS
    <BLANKLINE>
    <BLANKLINE>
    DATABASE_ENGINE = 'sqlite3'
    DATABASE_NAME = 'storage.db'
    DATABASE_USER = ''
    DATABASE_PASSWORD = ''
    DATABASE_HOST = ''
    DATABASE_PORT = ''
    <BLANKLINE>
    TIME_ZONE = 'America/Chicago'
    <BLANKLINE>
    LANGUAGE_CODE = 'en-us'
    <BLANKLINE>
    MEDIA_ROOT = '.../static'
    <BLANKLINE>
    MEDIA_URL = '/media/'
    <BLANKLINE>
    ADMIN_MEDIA_PREFIX = '/admin_media/'
    <BLANKLINE>
    SECRET_KEY = '...'
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.middleware.doc.XViewMiddleware',
    )
    <BLANKLINE>
    ROOT_URLCONF = 'dummydjangoprj.urls'
    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.admin',
    )
    <BLANKLINE>
    TEMPLATE_LOADERS = (
        'django.template.loaders.filesystem.load_template_source',
        'django.template.loaders.app_directories.load_template_source',
    )
    <BLANKLINE>
    TEMPLATE_DIRS = (
        '.../dummydjangoprj/templates',
    )
    <BLANKLINE>
    EMAIL_HOST = 'localhost'
    EMAIL_PORT = 25
    EMAIL_USE_TLS = False
    <BLANKLINE>
    CACHE_BACKEND = 'locmem:///'
    CACHE_TIMEOUT = 60*5
    CACHE_PREFIX = 'Z'

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('.../parts/django/settings.py')

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

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
    ... 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
    ... """)

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', 'settings.py')
    ADMINS = (
    <BLANKLINE>
        ('John Smith', 'root@localhost'),
    )
    MANAGERS = ADMINS
    <BLANKLINE>
    <BLANKLINE>
    DATABASE_ENGINE = 'sqlite3'
    DATABASE_NAME = 'storage.db'
    DATABASE_USER = ''
    DATABASE_PASSWORD = ''
    DATABASE_HOST = ''
    DATABASE_PORT = ''
    <BLANKLINE>
    TIME_ZONE = 'America/Chicago'
    <BLANKLINE>
    LANGUAGE_CODE = 'en-us'
    <BLANKLINE>
    MEDIA_ROOT = '.../static'
    <BLANKLINE>
    MEDIA_URL = '/media/'
    <BLANKLINE>
    ADMIN_MEDIA_PREFIX = '/admin_media/'
    <BLANKLINE>
    SECRET_KEY = '...'
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.middleware.doc.XViewMiddleware',
    )
    <BLANKLINE>
    ROOT_URLCONF = 'dummydjangoprj.urls'
    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.admin',
    )
    <BLANKLINE>
    TEMPLATE_LOADERS = (
        'django.template.loaders.filesystem.load_template_source',
        'django.template.loaders.app_directories.load_template_source',
    )
    <BLANKLINE>
    TEMPLATE_DIRS = (
        '.../dummydjangoprj/templates',
    )
    <BLANKLINE>
    EMAIL_HOST = 'localhost'
    EMAIL_PORT = 25
    EMAIL_USE_TLS = False
    <BLANKLINE>
    CACHE_BACKEND = 'locmem:///'
    CACHE_TIMEOUT = 60*5
    CACHE_PREFIX = 'Z'
    <BLANKLINE>
    <BLANKLINE>
    # Extension template %s
    <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
    ... 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
    ... """)


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', 'settings.py')
    # Total override
    FOODS = (
        'spam',
        'spam',
        'eggs',
        'spam',
    )

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

.. _Tempita: http://pythonpaste.org/Tempita/

.. _`mr.developer`: http://pypi.python.org/pypi/mr.developer

.. [#] 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.


