Friday 2 December 2011

Django + Nginx and shared media

I've been struggling with this for a couple of days - so I'm sticking them here for memoryade. I'm building a site from Django, using Nginx as the front end.

Maybe I'm suffering from Apache hangovers, but I've been having problems getting a nice solution to URLs mapped to views based off the same template. I think things were confused by also wanting to map the root of the site to a Django URL of '/'. Probably bad form, but I basically used my 'index.html' as a template, with Django style {%...%} blocks to be overridden by templates for each section. This means I have a Django project with no apps defined at the beginning, save for admin - which has it's own media mapping in Nginx.

The problem was that I managed to configure Nginx to map to '/' - and I could get Django to respond with different views in the root app - things like 'index' and 'projects' - but I would loose all the media. They would always append their part of the URL when looking for media like images and style-sheets. So if I went to 'projects' all links to 'media/image.png' would end up as 'projects/media/image.png' - I think Django expects all apps to contain their own media directory. So partly through thinking that I was messing with things having defined views in the default Django app, and mapped it to '/' - spent 2 days trying all sorts of Nginx and 'urls.py' configurations.

The answer was stupid - edit the original template to use links mapped to '/' so instead of "media/image.png" put "/media/image.png" - relative to the root, rather than the section your in.

So this is what I ended up with:

urls.py

urlpatterns = patterns('',
    ('^$', index),
    ('^projects/$', projects),
    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)

views.py

def index(request):
return render_to_response('home.html', {'content': "HELLO THERE!"})
def projects(request):
return render_to_response('home.html', {'content': "Projects"}) 
I'm using home.html twice, just adjusting the content with different views. 'home.html' looks like this
{% extends "index.html" %}
{%block content%} {{content}} {%endblock%} 

Then the Nginx configuration just looks like this:

server {
#listen   80; ## listen for ipv4; this line is default and implied
#listen   [::]:80 default ipv6only=on; ## listen for ipv6
root /home/me/Projects/mysite;
#index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
location  /media {
alias /home/me/Projects/mysite/media ;
}
location /admin/media {
alias /home/me/Projects/mysite/mysite/admin/media;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
#try_files $uri $uri/ /index.html;
fastcgi_pass 127.0.0.1:8000;
fastcgi_param PATH_INFO $fastcgi_script_name;
                fastcgi_param REQUEST_METHOD $request_method;
                fastcgi_param QUERY_STRING $query_string;
                fastcgi_param CONTENT_TYPE $content_type;
                fastcgi_param CONTENT_LENGTH $content_length;
                fastcgi_pass_header Authorization;
                fastcgi_intercept_errors off;
fastcgi_param SERVER_NAME $server_name;
  fastcgi_param SERVER_PORT 80;
fastcgi_param SERVER_PROTOCOL wsgi;
}
}


I added the last three myself - I hadn't seen them used anywhere - but it stopped my logs being full of error about them missing....

This approach works perfectly, and can be moved around, chopped up onto different servers etc...