Django

A 1-post collection

Deploying Django 1.7 (Python 3) on Ubuntu 14.04

Since the DigitalOcean guide is out of date, here's a new one as short as possible using the default sqlite instead of postgres for simplicity.

Patch:

$ sudo apt-get update
$ sudo apt-get upgrade

We'll use pyvenv-3.4 because virtualenv is for Python 2.
It was shipped in Ubuntu 14.04 with a broken pip, so we'll repair that as well as create our virtual environment at /opt/djenv:

$ pyvenv-3.4 --without-pip /opt/djenv
$ cd /opt/djenv
$ source bin/activate
(djenv) $ curl https://bootstrap.pypa.io/get-pip.py | python

(deactivate exits the virtual environment and source /opt/djenv/bin/activate enters it)

Install django and gunicorn much like Robin Winslow describes on his blog:

(djenv) $ which pip         # check pip points to the right place
/opt/djenv/bin/pip
(djenv) $ pip install django django-extensions     # install django dependencies
(djenv) $ pip install gunicorn
(djenv) $ pip freeze        # see our installed dependencies
Django==1.7.1
django-extensions==1.4.9
gunicorn==19.1.1
six==1.8.0
(djenv) $ django-admin.py startproject projectname  # create new django project
(djenv) $ pip freeze > projectname/requirements.txt # save dependencies into project

The Django 1.7 tutorials haven't quite caught up, when you deploy you'll need to add 127.0.0.1 or localhost to ALLOWED_HOSTS within settings.py or you'll get a Bad Request (400) error when DEBUG = False, answered in this Stack Overflow question.

ALLOWED_HOSTS = ['127.0.0.1', 'localhost']

The deployed static file setup can also be a little confusing. You'll create a new, empty folder on your webserver named static wherever you'd like - root project folder perhaps, alongside manage.py. Specify the full path to this folder within your settings.py as STATIC_ROOT, and whatever relative URL you desire and will be configuring within nginx as STATIC_URL, e.g.:

STATIC_URL = '/static/'
STATIC_ROOT = '/opt/djenv/projectname/static/'

Whenever you change your actual static files, likely located in paths such as /opt/djenv/projectname/appname/static/appname/ and /opt/djenv/projectname/otherappname/static/otherappname/ you need to re-run the collectstatic command so they'll all be gathered and automatically copied into the folder we created - you'll never do this manually:

(djenv) /opt/djenv/projectname$ ./manage.py collectstatic

And of course you must have nginx configured to serve both django running on gunicorn and static files, something like:

server {
    server_name yourdomainorip.com;

    access_log off;

    location /static/ {
        alias /opt/djenv/static/;
    }

    location / {
            proxy_pass http://127.0.0.1:8001;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Real-IP $remote_addr;
            add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
    }
}

Launching gunicorn with gunicorn_django is deprecated, instead of gunicorn_django --workers=3 --bind yourdomainorip.com:8001 you'll want to use something like:

(djenv) /opt/djenv/projectname$ gunicorn --env DJANGO_SETTINGS_MODULE=projectname.settings projectname.wsgi:application --bind 127.0.0.1:8001

Finally you can configure gunicorn to launch automatically with an Upstart script /etc/init/gunicorn.conf:

start on startup

script
  . /opt/djenv/bin/activate
  cd /opt/djenv/projectname
  gunicorn --env DJANGO_SETTINGS_MODULE=projectname.settings projectname.wsgi:application --bind 127.0.0.1:8001
end script

You can also control it via command-line e.g.: start gunicorn stop gunicorn restart gunicorn.

Discussion