From 9346ffe802721c0ed688232f364ec6baac997830 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 13:08:38 -0400 Subject: [PATCH 01/21] use less bleeding edge django 1.5.x --- {{cookiecutter.repo_name}}/requirements/base.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/{{cookiecutter.repo_name}}/requirements/base.txt b/{{cookiecutter.repo_name}}/requirements/base.txt index 3561e67c..f387083c 100644 --- a/{{cookiecutter.repo_name}}/requirements/base.txt +++ b/{{cookiecutter.repo_name}}/requirements/base.txt @@ -1,5 +1,5 @@ -# Bleeding edge Django -https://github.com/django/django/archive/1.6b4.tar.gz +# Latest Django 1.5.x +django==1.5.4 # Configuration django-configurations==0.5.1 @@ -14,9 +14,7 @@ django-floppyforms==1.1 # Models South==0.8.2 -# hack so django-model-utils doesn't override Django 1.6b4 install -git+git://github.com/pydanny/django-model-utils@d71bf2db7581d91dcc8e0a66fd4859eb8969d256 - +django-model-utils==1.5.0 # images Pillow==2.1.0 From 057821303f6e0d07643d0dcc5d2801c1b10bf89f Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 13:22:41 -0400 Subject: [PATCH 02/21] Use the console.EmailBackend to avoid error: [Errno 61] Connection refused when trying to send email to a non-existent local SMTP server --- .../{{cookiecutter.repo_name}}/config/settings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index fda8b6d7..78722575 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -257,8 +257,7 @@ class Local(Common): ########## END INSTALLED_APPS ########## Mail settings - EMAIL_HOST = "localhost" - EMAIL_PORT = 1025 + EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' ########## End mail settings ########## django-debug-toolbar From 337269648d1102d6ded1c9a7967f0d6ad0729f15 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 13:27:05 -0400 Subject: [PATCH 03/21] Make the deploy to Heroku instructions nicely formatted. --- {{cookiecutter.repo_name}}/README.rst | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/{{cookiecutter.repo_name}}/README.rst b/{{cookiecutter.repo_name}}/README.rst index 51bf9c64..3c71050f 100644 --- a/{{cookiecutter.repo_name}}/README.rst +++ b/{{cookiecutter.repo_name}}/README.rst @@ -9,21 +9,25 @@ LICENSE: BSD Deployment ------------ -* heroku create -* heroku addons:add heroku-postgresql:dev -* heroku addons:add pgbackups -* heroku addons:add sendgrid:starter -* heroku addons:add memcachier:dev -* heroku pg:promote HEROKU_POSTGRESQL_COLOR -* heroku config:add DJANGO_CONFIGURATION=Production -* heroku config:add DJANGO_SECRET_KEY=RANDOM_SECRET_KEY -* heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID -* heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY -* heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET -* git push heroku master -* heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings -* heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings -* heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings +Run these commands to deploy the project to Heroku:: + +.. code-block:: bash + + $ heroku create + $ heroku addons:add heroku-postgresql:dev + $ heroku addons:add pgbackups + $ heroku addons:add sendgrid:starter + $ heroku addons:add memcachier:dev + $ heroku pg:promote HEROKU_POSTGRESQL_COLOR + $ heroku config:add DJANGO_CONFIGURATION=Production + $ heroku config:add DJANGO_SECRET_KEY=RANDOM_SECRET_KEY + $ heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID + $ heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY + $ heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET + $ git push heroku master + $ heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings Run this script: (TODO - automate this) From a6eefe40b9af7fd7bafd42faab074797b54576c9 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 14:45:46 -0400 Subject: [PATCH 04/21] make it possible to serve up static assets using dj-static instead of S3 --- {{cookiecutter.repo_name}}/Procfile | 2 +- .../requirements/production.txt | 3 +- .../config/settings.py | 54 +++++++++++-------- .../{{cookiecutter.repo_name}}/config/wsgi.py | 4 +- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/{{cookiecutter.repo_name}}/Procfile b/{{cookiecutter.repo_name}}/Procfile index 0a678489..3cdbd490 100644 --- a/{{cookiecutter.repo_name}}/Procfile +++ b/{{cookiecutter.repo_name}}/Procfile @@ -1 +1 @@ -web: python {{cookiecutter.repo_name}}/manage.py run_gunicorn --settings=config.settings -b "0.0.0.0:$PORT" -w 3 \ No newline at end of file +web: gunicorn {{cookiecutter.repo_name}}/config/wsgi.py \ No newline at end of file diff --git a/{{cookiecutter.repo_name}}/requirements/production.txt b/{{cookiecutter.repo_name}}/requirements/production.txt index dcaeea79..72151eb2 100644 --- a/{{cookiecutter.repo_name}}/requirements/production.txt +++ b/{{cookiecutter.repo_name}}/requirements/production.txt @@ -5,4 +5,5 @@ gunicorn==0.17.4 django-storages==1.1.4 gevent==0.13.8 -boto==2.9.5 \ No newline at end of file +boto==2.9.5 +dj-static==0.0.5 \ No newline at end of file diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index 78722575..b77d776c 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -326,32 +326,42 @@ class Production(Common): INSTALLED_APPS += ("gunicorn", ) - ########## STORAGE CONFIGURATION - # See: http://django-storages.readthedocs.org/en/latest/index.html - INSTALLED_APPS += ( - 'storages', - ) + try: # serve static assets using S3 + ########## STORAGE CONFIGURATION + # See: http://django-storages.readthedocs.org/en/latest/index.html + INSTALLED_APPS += ( + 'storages', + ) - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' + # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - AWS_ACCESS_KEY_ID = values.SecretValue() - AWS_SECRET_ACCESS_KEY = values.SecretValue() - AWS_STORAGE_BUCKET_NAME = values.SecretValue() - AWS_AUTO_CREATE_BUCKET = True - AWS_QUERYSTRING_AUTH = False + # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + AWS_ACCESS_KEY_ID = values.SecretValue() + AWS_SECRET_ACCESS_KEY = values.SecretValue() + AWS_STORAGE_BUCKET_NAME = values.SecretValue() + AWS_AUTO_CREATE_BUCKET = True + AWS_QUERYSTRING_AUTH = False - # AWS cache settings, don't change unless you know what you're doing: - AWS_EXPIREY = 60 * 60 * 24 * 7 - AWS_HEADERS = { - 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, - AWS_EXPIREY) - } + # AWS cache settings, don't change unless you know what you're doing: + AWS_EXPIREY = 60 * 60 * 24 * 7 + AWS_HEADERS = { + 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, + AWS_EXPIREY) + } + + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url + STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + ########## END STORAGE CONFIGURATION + except: # serve static assets using wsgi + PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) + STATIC_ROOT = 'staticfiles' + STATIC_URL = '/static/' + + STATICFILES_DIRS = ( + join(PROJECT_PATH, 'static'), + ) - # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url - STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME - ########## END STORAGE CONFIGURATION ########## EMAIL DEFAULT_FROM_EMAIL = values.Value( diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py index 0225ba19..e918d461 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py @@ -26,7 +26,9 @@ os.environ.setdefault("DJANGO_CONFIGURATION", "Local") # file. This includes Django's development server, if the WSGI_APPLICATION # setting points here. from configurations.wsgi import get_wsgi_application -application = get_wsgi_application() +from dj_static import Cling + +application = Cling(get_wsgi_application()) # Apply WSGI middleware here. # from helloworld.wsgi import HelloWorldApplication From 0474a4f01e9995eeb106fc6a32a2c4c5ca34eb4e Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 14:45:59 -0400 Subject: [PATCH 05/21] ignore the staticfiles dir and .DS_Store --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3f36ae8c..003c6a05 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ *.pot *.pyc local_settings.py -repo_name \ No newline at end of file +repo_name +staticfiles +.DS_Store \ No newline at end of file From 1be9a48aa07b4f2e4ba76e6f311aff6da7b9bcde Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 14:46:14 -0400 Subject: [PATCH 06/21] move the deploy docs to docs/deploy.rst --- {{cookiecutter.repo_name}}/README.rst | 33 +---- {{cookiecutter.repo_name}}/docs/deploy.rst | 137 ++++++++++++++++++++- 2 files changed, 137 insertions(+), 33 deletions(-) diff --git a/{{cookiecutter.repo_name}}/README.rst b/{{cookiecutter.repo_name}}/README.rst index 3c71050f..e9ee8726 100644 --- a/{{cookiecutter.repo_name}}/README.rst +++ b/{{cookiecutter.repo_name}}/README.rst @@ -6,35 +6,4 @@ LICENSE: BSD -Deployment ------------- - -Run these commands to deploy the project to Heroku:: - -.. code-block:: bash - - $ heroku create - $ heroku addons:add heroku-postgresql:dev - $ heroku addons:add pgbackups - $ heroku addons:add sendgrid:starter - $ heroku addons:add memcachier:dev - $ heroku pg:promote HEROKU_POSTGRESQL_COLOR - $ heroku config:add DJANGO_CONFIGURATION=Production - $ heroku config:add DJANGO_SECRET_KEY=RANDOM_SECRET_KEY - $ heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID - $ heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY - $ heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET - $ git push heroku master - $ heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings - $ heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings - $ heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings - -Run this script: (TODO - automate this) - -.. code-block:: python - - from django.contrib.sites.models import Site - site = Site.objects.get() - site.domain = "{{cookiecutter.domain_name}}" - site.name = "{{cookiecutter.project_name}}" - site.save() +For instructions on how to deploy this project to Heroku, look in docs/deploy.rst. diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index 1e642c79..cf229ef5 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -1,4 +1,139 @@ Deploy ======== -This is where you describe how the project is deployed in production. +From within your project's directory, run these commands to deploy the project to Heroku:: + +.. code-block:: bash + + $ heroku create {{cookiecutter.project_name}} + Creating {{cookiecutter.project_name}}... done, stack is cedar + http://{{cookiecutter.project_name}}.herokuapp.com/ | git@heroku.com:{{cookiecutter.project_name}}.git + + $ git init + Initialized empty Git repository in /path/to/your/project/{{cookiecutter.project_name}}/.git/ + + +Add the Heroku git repo as a remote, so that we can push to it. + +.. code-block:: bash + + $ git remote add heroku git@heroku.com:{{cookiecutter.project_name}}.git + + +Add a PostgreSQL database. Note that you will probably get a color other than "GOLD". This is normal. + +.. code-block:: bash + + $ heroku addons:add heroku-postgresql:dev + Adding heroku-postgresql:dev on {{cookiecutter.project_name}}... done, v3 (free) + Attached as HEROKU_POSTGRESQL_GOLD_URL + Database has been created and is available + ! This database is empty. If upgrading, you can transfer + ! data from another database with pgbackups:restore. + +Add pgbackups to handle backups of the PostgreSQL database:: + +.. code-block:: bash + + $ heroku addons:add pgbackups + Adding pgbackups on {{cookiecutter.project_name}}... done, v4 (free) + You can now use "pgbackups" to backup your databases or import an external backup. + Use `heroku addons:docs pgbackups` to view documentation. + +Add sendgrid to handle the sending of emails:: + +.. code-block:: bash + + $ heroku addons:add sendgrid:starter + Adding sendgrid:starter on {{cookiecutter.project_name}}... done, v5 (free) + Use `heroku addons:docs sendgrid` to view documentation. + +Add memcachier for memcached service:: + +.. code-block:: bash + + $ heroku addons:add memcachier:dev + Adding memcachier:dev on {{cookiecutter.project_name}}... done, v7 (free) + MemCachier is now up and ready to go. Happy bananas! + Use `heroku addons:docs memcachier` to view documentation. + +Promote the database you just created. Please note that your database might be called something other than "GOLD". + +.. code-block:: bash + + $ heroku pg:promote HEROKU_POSTGRESQL_GOLD + Promoting HEROKU_POSTGRESQL_GOLD_URL to DATABASE_URL... done + +Set the DJANGO_CONFIGURATION environment variable so that Heroku knows we're in production. + +.. code-block:: bash + + $ heroku config:add DJANGO_CONFIGURATION=Production + Setting config vars and restarting {{cookiecutter.project_name}}... done, v8 + DJANGO_CONFIGURATION: Production + +Don't forget to replace the secret key with a random string. + +.. code-block:: bash + + $ heroku config:add DJANGO_SECRET_KEY='!!!REPLACE-ME!!!' + Setting config vars and restarting {{cookiecutter.project_name}}... done, v9 + DJANGO_SECRET_KEY: abcdefghijklmnopqrstuvwxyz + +If you're using AWS S3 to serve up static assets, then you need to set these values. + +.. code-block:: bash + + $ heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID + $ heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY + $ heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET + +Commit all the files in your project, and now we're finally ready to push the code to Heroku! + +.. code-block:: bash + + $ git commit -a + $ git push heroku master + Counting objects: 75, done. + Delta compression using up to 8 threads. + Compressing objects: 100% (67/67), done. + Writing objects: 100% (75/75), 28.12 KiB, done. + Total 75 (delta 4), reused 0 (delta 0) + + -----> Python app detected + -----> No runtime.txt provided; assuming python-2.7.4. + -----> Preparing Python runtime (python-2.7.4) + -----> Installing Distribute (0.6.36) + -----> Installing Pip (1.3.1) + -----> Noticed pylibmc. Bootstrapping libmemcached. + -----> Installing dependencies using Pip (1.3.1) + ... + Successfully installed pylibmc django django-configurations django-secure django-cache-url dj-database-url django-braces django-crispy-forms django-floppyforms South django-model-utils Pillow django-allauth psycopg2 unicode-slugify django-autoslug django-avatar gunicorn django-storages gevent boto six python-openid requests-oauthlib requests django-appconf greenlet oauthlib + Cleaning up... + -----> Discovering process types + Procfile declares types -> web + + -----> Compiled slug size: 40.8MB + -----> Launching... done, v10 + http://{{cookiecutter.project_name}}.herokuapp.com deployed to Heroku + + To git@heroku.com:{{cookiecutter.project_name}}.git + * [new branch] master -> master + + $ heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings + +TODO: Explain how to serve static files with dj-static_. + +.. _dj-static: https://github.com/kennethreitz/dj-static + +Run this script: (TODO - automate this) + +.. code-block:: python + + from django.contrib.sites.models import Site + site = Site.objects.get() + site.domain = "{{cookiecutter.domain_name}}" + site.name = "{{cookiecutter.project_name}}" + site.save() From deb0221c3dd9e43e5ba74c10c350ce37ed1ee0b4 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 14:46:23 -0400 Subject: [PATCH 07/21] fix a missing cookiecutter prefix --- {{cookiecutter.repo_name}}/docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.repo_name}}/docs/index.rst b/{{cookiecutter.repo_name}}/docs/index.rst index 58b9d7f4..a212be82 100644 --- a/{{cookiecutter.repo_name}}/docs/index.rst +++ b/{{cookiecutter.repo_name}}/docs/index.rst @@ -1,9 +1,9 @@ -.. {{ project_name }} documentation master file, created by +.. {{ cookiecutter.project_name }} documentation master file, created by sphinx-quickstart on Sun Feb 17 11:46:20 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to {{ project_name }}'s documentation! +Welcome to {{ cookiecutter.project_name }}'s documentation! ==================================== Contents: From 063a6332a3ec6920c2e51de3c845e5be076b1871 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 15:36:05 -0400 Subject: [PATCH 08/21] evaluate the AWS values first --- .../config/settings.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index b77d776c..b3c6fcf1 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -327,15 +327,6 @@ class Production(Common): INSTALLED_APPS += ("gunicorn", ) try: # serve static assets using S3 - ########## STORAGE CONFIGURATION - # See: http://django-storages.readthedocs.org/en/latest/index.html - INSTALLED_APPS += ( - 'storages', - ) - - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings AWS_ACCESS_KEY_ID = values.SecretValue() AWS_SECRET_ACCESS_KEY = values.SecretValue() @@ -350,8 +341,21 @@ class Production(Common): AWS_EXPIREY) } + ########## STORAGE CONFIGURATION + # See: http://django-storages.readthedocs.org/en/latest/index.html + INSTALLED_APPS += ( + 'storages', + ) + + # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + + # TODO: check that this actually works + MEDIA_URL = 'https://s3.amazonaws.com/%s/media/' % AWS_STORAGE_BUCKET_NAME + ########## END STORAGE CONFIGURATION except: # serve static assets using wsgi PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) From 21af4cf2b85cb94533ba2dd60ba72de19cb41027 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Thu, 26 Sep 2013 15:38:13 -0400 Subject: [PATCH 09/21] set the python path in the procfile, and use foreman to start the server --- {{cookiecutter.repo_name}}/Procfile | 2 +- .../{{cookiecutter.repo_name}}/config/wsgi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.repo_name}}/Procfile b/{{cookiecutter.repo_name}}/Procfile index 3cdbd490..ada25676 100644 --- a/{{cookiecutter.repo_name}}/Procfile +++ b/{{cookiecutter.repo_name}}/Procfile @@ -1 +1 @@ -web: gunicorn {{cookiecutter.repo_name}}/config/wsgi.py \ No newline at end of file +web: gunicorn --pythonpath="$PWD/{{cookiecutter.repo_name}}" config.wsgi:application \ No newline at end of file diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py index e918d461..00135145 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py @@ -19,7 +19,7 @@ import os # if running multiple sites in the same mod_wsgi process. To fix this, use # mod_wsgi daemon mode with each site in its own daemon process, or use # os.environ["DJANGO_SETTINGS_MODULE"] = "{{ repo_name }}.settings" -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ cookiecutter.repo_name }}.config.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") os.environ.setdefault("DJANGO_CONFIGURATION", "Local") # This application object is used by any WSGI server configured to use this From e90282111707bf2ceb20ec10415509cdb9645802 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 10:48:47 -0400 Subject: [PATCH 10/21] make the docs less verbose so they're more readable in plaintext --- {{cookiecutter.repo_name}}/docs/deploy.rst | 26 ++++++---------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index cf229ef5..849fbc6e 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -42,55 +42,41 @@ Add pgbackups to handle backups of the PostgreSQL database:: Add sendgrid to handle the sending of emails:: -.. code-block:: bash - $ heroku addons:add sendgrid:starter Adding sendgrid:starter on {{cookiecutter.project_name}}... done, v5 (free) Use `heroku addons:docs sendgrid` to view documentation. Add memcachier for memcached service:: -.. code-block:: bash - $ heroku addons:add memcachier:dev Adding memcachier:dev on {{cookiecutter.project_name}}... done, v7 (free) MemCachier is now up and ready to go. Happy bananas! Use `heroku addons:docs memcachier` to view documentation. -Promote the database you just created. Please note that your database might be called something other than "GOLD". - -.. code-block:: bash +Promote the database you just created. Please note that your database might be called something other than "GOLD":: $ heroku pg:promote HEROKU_POSTGRESQL_GOLD Promoting HEROKU_POSTGRESQL_GOLD_URL to DATABASE_URL... done -Set the DJANGO_CONFIGURATION environment variable so that Heroku knows we're in production. - -.. code-block:: bash +Set the DJANGO_CONFIGURATION environment variable so that Heroku knows we're in production:: $ heroku config:add DJANGO_CONFIGURATION=Production Setting config vars and restarting {{cookiecutter.project_name}}... done, v8 DJANGO_CONFIGURATION: Production -Don't forget to replace the secret key with a random string. - -.. code-block:: bash +Don't forget to replace the secret key with a random string:: $ heroku config:add DJANGO_SECRET_KEY='!!!REPLACE-ME!!!' Setting config vars and restarting {{cookiecutter.project_name}}... done, v9 DJANGO_SECRET_KEY: abcdefghijklmnopqrstuvwxyz -If you're using AWS S3 to serve up static assets, then you need to set these values. - -.. code-block:: bash +If you're using AWS S3 to serve up static assets, then you need to set these values:: $ heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID $ heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY $ heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET -Commit all the files in your project, and now we're finally ready to push the code to Heroku! - -.. code-block:: bash +Commit all the files in your project, and now we're finally ready to push the code to Heroku!:: $ git commit -a $ git push heroku master @@ -120,6 +106,8 @@ Commit all the files in your project, and now we're finally ready to push the co To git@heroku.com:{{cookiecutter.project_name}}.git * [new branch] master -> master +Run the syncdb, migrate and collectstatic Django management commands:: + $ heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings $ heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings $ heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings From 76a604da176433a59c0ef34fbcb5f561426d2b43 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 10:49:17 -0400 Subject: [PATCH 11/21] since the settings.py file is nested inside the config/ dir, we need to go up one directory to find the static files --- .../{{cookiecutter.repo_name}}/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index b3c6fcf1..fac7879f 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -281,7 +281,7 @@ class Local(Common): # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS STATICFILES_DIRS = ( - join(BASE_DIR, 'static'), + join(BASE_DIR, '..', 'static'), ) # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders From d6acaedee026453391da08755acbb70a33bc3ae8 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 11:00:17 -0400 Subject: [PATCH 12/21] change timezone from LA to NY --- .../{{cookiecutter.repo_name}}/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index fac7879f..6b4fc41c 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -129,7 +129,7 @@ class Common(Configuration): ########## GENERAL CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone - TIME_ZONE = 'America/Los_Angeles' + TIME_ZONE = 'America/New_York' # See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code LANGUAGE_CODE = 'en-us' From 9dc2de43015050e35928d5a9ca58705991a908ab Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 11:00:49 -0400 Subject: [PATCH 13/21] don't use S3 to serve static assets for now, and serve them up using dj-static --- .../config/settings.py | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index 6b4fc41c..e0e07fdf 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -284,6 +284,13 @@ class Local(Common): join(BASE_DIR, '..', 'static'), ) + # as recommended on # https://devcenter.heroku.com/articles/django-assets + # PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) + + # STATICFILES_DIRS = ( + # join(PROJECT_PATH, 'static'), + # ) + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', @@ -326,46 +333,63 @@ class Production(Common): INSTALLED_APPS += ("gunicorn", ) - try: # serve static assets using S3 - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - AWS_ACCESS_KEY_ID = values.SecretValue() - AWS_SECRET_ACCESS_KEY = values.SecretValue() - AWS_STORAGE_BUCKET_NAME = values.SecretValue() - AWS_AUTO_CREATE_BUCKET = True - AWS_QUERYSTRING_AUTH = False + ########## STATIC FILE CONFIGURATION + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root + STATIC_ROOT = 'staticfiles' - # AWS cache settings, don't change unless you know what you're doing: - AWS_EXPIREY = 60 * 60 * 24 * 7 - AWS_HEADERS = { - 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, - AWS_EXPIREY) - } + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url + STATIC_URL = '/static/' - ########## STORAGE CONFIGURATION - # See: http://django-storages.readthedocs.org/en/latest/index.html - INSTALLED_APPS += ( - 'storages', - ) + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS + STATICFILES_DIRS = ( + join(BASE_DIR, '..', 'static'), + ) - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' + # as recommended on # https://devcenter.heroku.com/articles/django-assets + # PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) - # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url - STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + # STATICFILES_DIRS = ( + # join(PROJECT_PATH, 'static'), + # ) - # TODO: check that this actually works - MEDIA_URL = 'https://s3.amazonaws.com/%s/media/' % AWS_STORAGE_BUCKET_NAME + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders + STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + ) + + # TODO: check to see if AWS keys are set and if they are, serve static assets using S3 + # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + # AWS_ACCESS_KEY_ID = values.SecretValue() - ########## END STORAGE CONFIGURATION - except: # serve static assets using wsgi - PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) - STATIC_ROOT = 'staticfiles' - STATIC_URL = '/static/' + # AWS_SECRET_ACCESS_KEY = values.SecretValue() + # AWS_STORAGE_BUCKET_NAME = values.SecretValue() + # AWS_AUTO_CREATE_BUCKET = True + # AWS_QUERYSTRING_AUTH = False - STATICFILES_DIRS = ( - join(PROJECT_PATH, 'static'), - ) + # # AWS cache settings, don't change unless you know what you're doing: + # AWS_EXPIREY = 60 * 60 * 24 * 7 + # AWS_HEADERS = { + # 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, + # AWS_EXPIREY) + # } + # ########## STORAGE CONFIGURATION + # # See: http://django-storages.readthedocs.org/en/latest/index.html + # INSTALLED_APPS += ( + # 'storages', + # ) + + # # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + # STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' + + # # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url + # STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + + # # TODO: check that this actually works + # MEDIA_URL = 'https://s3.amazonaws.com/%s/media/' % AWS_STORAGE_BUCKET_NAME + + ########## END STORAGE CONFIGURATION ########## EMAIL DEFAULT_FROM_EMAIL = values.Value( From 31e7456cbc7082f8d7c4b6a2e705cb140f876e3d Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 11:01:17 -0400 Subject: [PATCH 14/21] reference docs to more info about dj-static --- .../{{cookiecutter.repo_name}}/config/wsgi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py index 00135145..a68c5a70 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py @@ -26,6 +26,9 @@ os.environ.setdefault("DJANGO_CONFIGURATION", "Local") # file. This includes Django's development server, if the WSGI_APPLICATION # setting points here. from configurations.wsgi import get_wsgi_application + +# serve static assets using wsgi +# https://devcenter.heroku.com/articles/django-assets from dj_static import Cling application = Cling(get_wsgi_application()) From 00da531b06c7ac0d92ac8bb9333cf2764f373d8e Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 20:16:54 -0400 Subject: [PATCH 15/21] clean up the docs --- {{cookiecutter.repo_name}}/docs/deploy.rst | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index 849fbc6e..2cba664e 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -1,29 +1,24 @@ Deploy ======== -From within your project's directory, run these commands to deploy the project to Heroku:: - -.. code-block:: bash +From within your project's directory, run this command to create a Heroku app:: $ heroku create {{cookiecutter.project_name}} Creating {{cookiecutter.project_name}}... done, stack is cedar http://{{cookiecutter.project_name}}.herokuapp.com/ | git@heroku.com:{{cookiecutter.project_name}}.git - $ git init - Initialized empty Git repository in /path/to/your/project/{{cookiecutter.project_name}}/.git/ +If you haven't cloned the project from an existing git repo, then you need to initialize it:: + $ cd {{cookiecutter.repo_name}} + $ git init + Initialized empty Git repository in /path/to/your/project/{{cookiecutter.repo_name}}/.git/ Add the Heroku git repo as a remote, so that we can push to it. -.. code-block:: bash - $ git remote add heroku git@heroku.com:{{cookiecutter.project_name}}.git - Add a PostgreSQL database. Note that you will probably get a color other than "GOLD". This is normal. -.. code-block:: bash - $ heroku addons:add heroku-postgresql:dev Adding heroku-postgresql:dev on {{cookiecutter.project_name}}... done, v3 (free) Attached as HEROKU_POSTGRESQL_GOLD_URL @@ -33,8 +28,6 @@ Add a PostgreSQL database. Note that you will probably get a color other than "G Add pgbackups to handle backups of the PostgreSQL database:: -.. code-block:: bash - $ heroku addons:add pgbackups Adding pgbackups on {{cookiecutter.project_name}}... done, v4 (free) You can now use "pgbackups" to backup your databases or import an external backup. From 0b40e9bd3cfab230a492893cfe6803a23c5a5cfb Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Fri, 27 Sep 2013 20:22:27 -0400 Subject: [PATCH 16/21] missed some formatting in the docs --- {{cookiecutter.repo_name}}/docs/deploy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index 2cba664e..f7e2a9ad 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -13,11 +13,11 @@ If you haven't cloned the project from an existing git repo, then you need to in $ git init Initialized empty Git repository in /path/to/your/project/{{cookiecutter.repo_name}}/.git/ -Add the Heroku git repo as a remote, so that we can push to it. +Add the Heroku git repo as a remote, so that we can push to it:: $ git remote add heroku git@heroku.com:{{cookiecutter.project_name}}.git -Add a PostgreSQL database. Note that you will probably get a color other than "GOLD". This is normal. +Add a PostgreSQL database. Note that you will probably get a color other than "GOLD". This is normal:: $ heroku addons:add heroku-postgresql:dev Adding heroku-postgresql:dev on {{cookiecutter.project_name}}... done, v3 (free) From d0e27716c934658bab5b36a963f3b5243c004c33 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Sat, 28 Sep 2013 22:07:40 -0400 Subject: [PATCH 17/21] added a pointer to app-specific documentation --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 10c5f779..1c1ccac0 100644 --- a/README.rst +++ b/README.rst @@ -83,8 +83,11 @@ Create a GitHub repo and push it there:: Now take a look at your repo. Awesome, right? -It's time to write the code!!! +If you check out your new application, you will see that you can find documentation in the "docs" directory that explains everything about your app as it exists in its initial cookiecutter state, including how to deploy it. +If you want to read the documentation from your browser instead of a text editing window, you'll have to compile it first. First, make sure that you have all of the local packages that you need (including the ones you need to generate this documentation) by running "pip install -r requirements/local.txt". Go into the docs directory and run "make html". That will generate html from the .rst files. If you point your browser to your local variant of "/path/to/my_cookiecutter_project/docs/_build/html/index.html". + +So, you now have a simple django application. Go forth and code! "Your Stuff" ------------- From da942d59d4020a303da200d35523b1fc595731f8 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Sat, 28 Sep 2013 22:47:24 -0400 Subject: [PATCH 18/21] added how to get database name to deployment docs --- {{cookiecutter.repo_name}}/docs/deploy.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index f7e2a9ad..5421fe52 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -46,6 +46,21 @@ Add memcachier for memcached service:: MemCachier is now up and ready to go. Happy bananas! Use `heroku addons:docs memcachier` to view documentation. +Look up the name of your database. You should have gotten the name as part of the feedback when you created the +database, but just in case you lost that record:: + + $ heroku pg + === HEROKU_POSTGRESQL_GOLD_URL (DATABASE_URL) + Plan: Dev + Status: available + Connections: 1 + PG Version: 9.2.4 + Created: 2013-09-29 02:00 UTC + Data Size: 6.3 MB + Tables: 0 + Rows: 0/10000 (In compliance) + Fork/Follow: Unsupported + Promote the database you just created. Please note that your database might be called something other than "GOLD":: $ heroku pg:promote HEROKU_POSTGRESQL_GOLD From ee3936997bf32504f303f009c93e4c00fe77e768 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Sat, 28 Sep 2013 22:55:03 -0400 Subject: [PATCH 19/21] added instructions on how to run python from heroku --- {{cookiecutter.repo_name}}/docs/deploy.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index 5421fe52..eda5220c 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -124,7 +124,11 @@ TODO: Explain how to serve static files with dj-static_. .. _dj-static: https://github.com/kennethreitz/dj-static -Run this script: (TODO - automate this) +Open up a django shell on your heroku instance:: + + $ heroku run python {{cookiecutter.repo_name}}/manage.py shell --settings=config.settings + +Run the following lines of code from within that shell: (TODO - automate this) .. code-block:: python From 3b9e953e66da567ab43d2b2b0509d52d78ce59ee Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Sat, 28 Sep 2013 23:36:40 -0400 Subject: [PATCH 20/21] typo fix --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1c1ccac0..56aff527 100644 --- a/README.rst +++ b/README.rst @@ -85,7 +85,7 @@ Now take a look at your repo. Awesome, right? If you check out your new application, you will see that you can find documentation in the "docs" directory that explains everything about your app as it exists in its initial cookiecutter state, including how to deploy it. -If you want to read the documentation from your browser instead of a text editing window, you'll have to compile it first. First, make sure that you have all of the local packages that you need (including the ones you need to generate this documentation) by running "pip install -r requirements/local.txt". Go into the docs directory and run "make html". That will generate html from the .rst files. If you point your browser to your local variant of "/path/to/my_cookiecutter_project/docs/_build/html/index.html". +If you want to read the documentation from your browser instead of a text editing window, you'll have to compile it first. First, make sure that you have all of the local packages that you need (including the ones you need to generate this documentation) by running "pip install -r requirements/local.txt". Go into the docs directory and run "make html". That will generate html from the .rst files. Point your browser to your local variant of "/path/to/my_cookiecutter_project/docs/_build/html/index.html". So, you now have a simple django application. Go forth and code! From 1784b6db913edb6cdf67ec1d93f25184b6936dc0 Mon Sep 17 00:00:00 2001 From: Nate Aune Date: Mon, 7 Oct 2013 17:37:14 -0400 Subject: [PATCH 21/21] change README to point out that this template has been downgraded to Django 1.5, not 1.6 --- README.rst | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 56aff527..d8d6f0e2 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ A cookiecutter_ template for Django. Features --------- -* Cutting edge: For Django 1.6 and other bleeding edge stuff +* Cutting edge: For Django 1.5 and other bleeding edge stuff * Twitter Bootstrap 3 * AngularJS * Registration via django-allauth @@ -24,13 +24,6 @@ Constraints * PostgreSQL everywhere * Environment variables for configuration (This won't work with Apache/mod_wsgi) -Caution: Bleeding Edge Requirements ------------------------------------- - -The cookiecutter-django project is bleeding edge in that it uses unreleased versions of several packages like Django, -South, django-crispy-forms, django-avatar, and more. - -Consider yourself warned. Usage ------