This is how I actually got all this stuff to work together. It'll attrophy over time but as of May 2013 it works. Maybe I'll keep it up to date.
Generally, this document is a a mashup of the following tutorials.
These links were super handy along the way
We'll do the Django tutorial part first, then do the heroku steps necessary to keep it in sync.
brew install gnu-sed # this is because osx's sed is lame
We keep and eye toward repeatability and Heroku.
Here we set up our project name. This is the same name as the project on heroku (ie horseday.heroku.com). You don't have to set it up this way, but it is convenient for smaller projects.
export PROJ=horseday # You probably want to use something other than 'horseday'
cd ~/Projects/heroku # you might choose differently
mkdir $PROJ && cd $PROJ
cat <<EOF >.gitignore
venv
*pyc
EOF
git init
git add .
git commit -m "initial commit"
virtualenv venv --distribute
source venv/bin/activate
For me this takes about 60 seconds.
time pip install Django psycopg2 gunicorn dj-database-url django-storages boto
django-admin.py startproject $PROJ .
git add . && git commit -m "Add the $PROJ project."
python manage.py startapp polls
git add . && git commit -m "Add the poll app."
gsed -i '1aimport os' $PROJ/settings.py
git add . && git commit -m "Add imports to settings.py"
gsed -i "/^ALLOWED_HOSTS =/s/\[\]/[ '*' ]/" $PROJ/settings.py
gsed -i "/^DEBUG =/s/.*/DEBUG = 'True'==os.environ['DEBUG']/" $PROJ/settings.py
gsed -i "s/.*INSTALLED_APPS.*/&\n 'polls',/" $PROJ/settings.py
gsed -i "s/.*INSTALLED_APPS.*/&\n 'storages',/" $PROJ/settings.py
cat <<EOF >> $PROJ/settings.py
# Parse database configuration from DATABASE_URL
import dj_database_url
DATABASES['default'] = dj_database_url.config()
if not DEBUG:
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = STORAGE
S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
MEDIA_ROOT = 'media'
STATIC_ROOT = 'static'
STATIC_URL = S3_URL + STATIC_ROOT + '/'
MEDIA_URL = S3_URL + MEDIA_ROOT + '/'
ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/'
EOF
git add . && git commit -m "settings.py modifications"
cat <<EOF > polls/models.py
import datetime
from django.db import models
from django.utils import timezone
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.question
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __unicode__(self):
return self.choice_text
EOF
git add . && git commit -m "create polls/models.py"
sed -i "" "/'django.contrib.admin'/s/# //" $PROJ/settings.py
git add . && git commit -m "enable admin in settings.py"
sed -i "" -e '/from.*admin/s/# //' -e '/admin.autodiscover/s/# //' -e "/url.*admin\/'/s/# //" $PROJ/urls.py
git add . && git commit -m "enable admin in urls.py"
mkdir env
The env/common script sets up the common environment variables
cat <<EOF > env/common
source venv/bin/activate
export PROJ=$PROJ
export PYTHONPATH=$PWD # so oneoff scripts can run
EOF
git add . && git commit -m "create the env/common script"
The env/local script sets up the local environment variables
cat <<EOF > env/local
export DATABASE_URL=postgres://\$PROJ@localhost:5432/\$PROJ
export DJANGO_SETTINGS_MODULE=\$PROJ.settings
cat <<HMM > ~/.$PROJ.hmm
runserver run the server in the background
goadmin start a browser in the admin site
HMM
alias hmm="sort ~/.$PROJ.hmm"
echo "hmm for help"
EOF
git add . && git commit -m "create env/local script"
The env/debug script is used to set up the local development environment in debug mode.
cat <<EOF > env/debug
source env/common
source env/local
export DEBUG=True
function runserver { python manage.py runserver & }
function goadmin { open http://localhost:8000/admin/; }
EOF
git add . && git commit -m "create env/debug script"
The env/foreman script is used to set up the local development environment in debug mode using the same server as heroku.
cat <<EOF > env/foreman
source env/common
source env/local
export DEBUG=False
export AWS_STORAGE_BUCKET_NAME=\$PROJ
function runserver { foreman start -p 8000 & }
function goadmin { open http://localhost:8000/admin/; }
EOF
git add . && git commit -m "create env/foreman script"
source env/debug
psql -c "CREATE USER $PROJ;"
psql -c "CREATE DATABASE $PROJ;"
psql -c "GRANT ALL PRIVILEGES ON DATABASE $PROJ to $PROJ;"
psql -c "\list" # look for $PROJ in left column. Ctc means a user can Connect Create and create Temp
psql -c "\du" # shows users
python manage.py syncdb # yes/YOURNAME/YOU@EMAIL.COM/YOURPASS
psql $PROJ -c "\dt" | grep polls
mkdir oneoff
cat <<EOF > oneoff/01.py
from polls.models import Poll, Choice
from django.utils import timezone
p = Poll(question="What's new?", pub_date=timezone.now())
p.quesiton = "What's up?"
p.save()
p.choice_set.create(choice_text='Not much', votes=0)
p.choice_set.create(choice_text='The sky', votes=1)
p.save()
EOF
git add . && git commit -m "create oneoff script to create data"
python oneoff/01.py
echo "from polls.models import Poll; Poll.objects.all()" | python manage.py shell
cat <<EOF > Procfile
web: gunicorn $PROJ.wsgi
EOF
git add . && git commit -m "Create Procfile"
source env/debug
runserver
goadmin
kill %1 # to kill the local debug server
source env/foreman
python manage.py collectstatic
source env/foreman
runserver
goadmin
kill %1 # to kill the foreman server
pip freeze > requirements.txt
git add . && git commit -m "create the requirements.txt"
cat <<EOF >> $PROJ/settings.py
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
EOF
git add . && git commit -m "configure heroku https in settings.py"
heroku create $PROJ
git remote add heroku git@heroku.com:$PROJ.git
# Note: move on if 'fatal: remote heroku already exists'
git push heroku master
heroku config:add \
DEBUG=False \
PYTHONPATH=/app \
DJANGO_SETTINGS_MODULE=$PROJ.settings \
AWS_STORAGE_BUCKET_NAME=$PROJ \
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
heroku ps:scale web=1
heroku run python manage.py syncdb
heroku run python oneoff/01.py
open http://horseday.herokuapp.com/admin/
cd ~/Projects/heroku
rm -rf $PROJ
psql -c "drop database $PROJ" && psql -c "drop user $PROJ"
heroku destroy $PROJ #--confirm $PROJ # get rid of the 1st
exit # get rid of environment
heroku logs # show me the logs
heroku run python manage.py shell # manage the heroku side from python shell
python manage.py sql polls # show sql for polls app