deploying a django app to heroku with easy static files on s3

django and s3

This project doesn't necessarily require heroku, but I've been interested in the service as an easy way to deploy apps. I really like the idea of not having to set up a server with nginx, postgres, gunicorn, etc. The convenience of deploying just by pushing from git is awesome.   One issue I ran into was a problem with serving static content like css, javascript and image files from heroku. I'm sure it is just a configuration problem, but this solution of using s3 to serve those files is the way you want to go in the long run. You can clone my sample project from github. git clone git@github.com:eknuth/django-heroku-s3-bootstrap-demo.git

Django on Heroku

Heroku used to be limited to ruby apps, but it has recently become much more flexible with the addition of the cedar stack. Now it supports python 2.7 and uses pip to install modules from a standard requirements.txt file.  They have also started offering a free 5mb postgresql db to go along with their current freemium offering, which is fantastic.  That doesn't sound like a lot of space, but you'd be surprised how much information you can cram into 5 mb.

Initial Setup

You can follow the django instructions from heroku here: http://devcenter.heroku.com/articles/django If you are using macosx, or virtualenv uses python 2.6 by default, I would suggest specifying 2.7 when you create the environment.  Heroku is using 2.7 and will complain about pip installing importlib which was not included by default in python 2.6. virtualenv --no-site-packages . -p /usr/local/bin/python2.7 Open the site on heroku using the "heroku open" command and verify that you get the django welcome screen.  You should have no urls configured.

Admin and Static Files

You should now have a working empty project.  Let's turn on the django admin app and fire up the development server. As a side note, this config will import settings from settings_local.py, if it exists and overwrite the ones in settings.py. This local settings file is not committed to the repo and won't get deployed to heroku. Follow the instructions to install the django admin app and then run manage.py syncdb and then runserver.  If everything went ok, you should be able to log in to your admin console. Now, commit and push your app to heroku.  Run the manage.py commands on heroku and magically your admin console is live on the web.

Twitter Bootstrap and view

Now, lets getting started with a template and a home view.  We can use the totally awesome twitter bootstrap project.  They have released a site/webapp html template, css and javascript package that provides everything you need to get started.  It is great for those that are less inclined to design. Create a templates folder in your project root.  Clone the project from github and copy hero.html from the examples directory into your new templates directory.


Now let's create a view and wire up that template to a url.  Now is the time to add on a django project that will make things easier for us.  I really love the render_to decorator from django-annoying.  Run "pip install django-annoying", and it will be ready to go.  Don't forget to save your updated requirements with a "pip freeze >requirements.txt".

relative paths

Add the template dir and a little snippet of python to use relative paths. This will make life easier in the future. In the urls.py file, uncomment the following home view: url(r'^$', 'om.views.home', name='home'), And create a views.py file to grab the template and render it. Now when you open the app on your development server, you get an actual "hello world" page. Unfortunately we are missing the css file, so it is unstyled. In your root project, create a static folder and inside that create another folder named css. Copy bootstrap.css into static/css. To reference this css file, edit hero.html and change the line with bootstrap.css to this: <link href="css/bootstrap.css" rel="stylesheet"> In order to make the STATIC_URL template tag work, we need to update STATICFILES_DIRS in settings.py to: STATICFILES_DIRS = (        os.path.join(SITE_ROOT, 'static'),) We also need add some template context processors with this: TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.messages.context_processors.messages', 'django.contrib.auth.context_processors.auth', 'django.core.context_processors.static', ) When we open the page served from our development server, we are good to go. The page is styled and looks awesome.

Static Files on Heroku

Unfortunately when we push to heroku and open the page, we are back to the our css free look. Seems like heroku has no problem serving up the admin static files, but it has a problem finding our static directory. I'm sure it is just a matter of configuration, but honestly we really don't want to use our application server to serve up static files anyway. Images, css and javascript should all be coming out of something fast and light.

Enter S3

Amazon provides an excellent service for doing just that. Amazon's web services and the simple storage system are cheap and easy to use. There is also an excellent module for using s3 with django's static files app and the collectstatic management command with s3 as a backend. django-storages makes it incredibly simple to use s3 for our static files.

django-storages and boto

First we want to install the module with "pip install django-storages" and add 'storages' to your INSTALLED_APPS tuple and set your storage type with: STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage' We will also need to install boto, the python module for dealing with s3. Use "pip install boto" for that. Log in to the amazon web services management console and create a new bucket. Add the name of the bucket and your access keys to settings.py like this: AWS_ACCESS_KEY_ID = 'ACCESS KEY' AWS_SECRET_ACCESS_KEY = 'SECRET' AWS_STORAGE_BUCKET_NAME = 'django-blog

set up the s3 bucket

Look at the bucket properties and grab the url for the bucket. Update your STATIC_URL setting to point to the bucket. In this case it will look like this: STATIC_URL = 'https://s3.amazonaws.com/django-blog/'


Now run manage.py collectstatic and you will see all the files being copied to your bucket. Freeze your requirements file and commit your repo. Now when you push to heroku, you should see your website with the bootstrap.css file pulled from s3.