There is a newer version of this post. Information below is no longer valid!
There is a misconception about how static files (a.k.a media files) are handled in Django. Actually it is quite clearly documented here and here. Nevertheless a question about this comes up in the mailing-list or IRC channel frequently:
Where do I put my media files?
Django can’t find my foo.gif!
How can I link my CSS?
First of all, just to make it clear; just because a server returns a response body with an internal URL doesn’t necessarily mean it will be available on that server. It is one thing that your templates produce the correct URL to a media file and another thing that your server actually serves that resource on that URL. Django development server doesn’t automagically serve media files1.
Settings
There are three settings to get right: MEDIA_ROOT, MEDIA_URL and ADMIN_MEDIA_PREFIX. MEDIA_ROOT is the absolute filesystem path where your media files are. I usually set it like:
MEDIA_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'media')
This will set MEDIA_ROOT to point to the media directory in your project directory2. MEDIA_URL and ADMIN_MEDIA_PREFIX are URL’s:
MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'
With this setup, to serve admin media in production, all I need to do is to symlink media folder of admin app into my media directory. Of course you can set MEDIA_URL to point to another domain/subdomain. Such as http://media.mydomain.com/. But this way you can’t serve your media from development server.
URL Configuration
Add the following code snipplet at the end of your root urls.py:
1 if settings.DEBUG:
2 from django.views.static import serve
3 _media_url = settings.MEDIA_URL
4 if _media_url.startswith('/'):
5 _media_url = _media_url[1:]
6 urlpatterns += patterns('',
7 (r'^%s(?P<path>.*)$' % _media_url,
8 serve,
9 {'document_root': settings.MEDIA_ROOT}))
10 del(_media_url, serve)
settings.DEBUG == True doesn’t necessarily mean development server is running. But it is a good indicator since deploying with development server is not a good idea for many reasons. Notice here we don’t serve media unless MEDIA_URL is an absolute URL on our server.
Templates
Finally we need to specify media URL’s correctly. To avoid hard-coding media path we will be using {{ MEDIA_URL }} context variable in our templates. To have {{ MEDIA_URL }} included automatically in each template we need to do two things:
- Make sure you have
django.core.context_processors.media in your TEMPLATE_CONTEXT_PROCESSORS.
- Make sure each view is using a
RequestContext.
Afterwards all we need to do is to specify our media URL’s like this:
<img src="{{ MEDIA_URL }}img/header.jpeg" />
This will be translated to:
<img src="/media/img/header.jpeg" />
Bonus
While we are at it, why not serve our 500 and 404 pages statically. When DEBUG == True, 500 (server error) and 404 (not found) situations are handled with special debugging views. So there’s no chance to test your error pages. Add the following code, just like static serving code:
1 if settings.DEBUG:
2 urlpatterns += patterns('',
3 (r'^404/',
4 'django.views.generic.simple.' \
5 'direct_to_template',
6 {'template': '404.html'}),
7 (r'^500/',
8 'django.views.generic.simple.' \
9 'direct_to_template',
10 {'template': '500.html'}))
Now when you visit /500/ and /404/ on your development server you will be served a fake error page.
1: There is an exception here. If you configured your settings correctly, development server will serve admin media.
2: Assuming your settings.py is directly inside your project directory, hence the __file__.
Related posts:
- My PyCon APAC 2011 Presentation: Optimizing Media Performance with django_compressor
- Working with files in Django – Part 1
- Working with files in Django – Part 2
- Working with files in Django – Part 3
-----
There is a [newer version of this post](/2011/11/working-with-files-in-django/). Information below is no longer valid!
-----
There is a misconception about how static files (a.k.a media files) are handled in Django. Actually it is quite clearly documented [here](http://docs.djangoproject.com/en/dev/howto/static-files/#module-django.views.static) and [here](http://docs.djangoproject.com/en/dev/howto/deployment/modpython/#id1). Nevertheless a question about this comes up in the mailing-list or IRC channel frequently:
> Where do I put my media files?
> Django can't find my `foo.gif`!
> How can I link my CSS?
First of all, just to make it clear; **just because a server returns a response body with an internal URL doesn't necessarily mean it will be available on that server**. It is one thing that your templates produce the correct URL to a media file and another thing that your server actually serves that resource on that URL. **Django development server doesn't automagically serve media files**1.
### Settings
There are three settings to get right: `MEDIA_ROOT`, `MEDIA_URL` and `ADMIN_MEDIA_PREFIX`. `MEDIA_ROOT` is the _absolute filesystem path_ where your media files are. I usually set it like:
MEDIA_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'media')
This will set `MEDIA_ROOT` to point to the `media` directory in your project directory2. `MEDIA_URL` and `ADMIN_MEDIA_PREFIX` are _URL's_:
MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'
With this setup, to serve admin media in production, all I need to do is to symlink media folder of admin app into my media directory. Of course you can set `MEDIA_URL` to point to another domain/subdomain. Such as `http://media.mydomain.com/`. But this way you can't serve your media from development server.
### URL Configuration
Add the following code snipplet at the end of your root `urls.py`:
1 if settings.DEBUG:
2 from django.views.static import serve
3 _media_url = settings.MEDIA_URL
4 if _media_url.startswith('/'):
5 _media_url = _media_url[1:]
6 urlpatterns += patterns('',
7 (r'^%s(?P<path>.*)$' % _media_url,
8 serve,
9 {'document_root': settings.MEDIA_ROOT}))
10 del(_media_url, serve)
`settings.DEBUG == True` doesn't necessarily mean development server is running. But it is a good indicator since deploying with development server is not a good idea for many reasons. Notice here we don't serve media unless `MEDIA_URL` is an absolute URL on our server.
### Templates
Finally we need to specify media URL's correctly. To avoid hard-coding media path we will be using `{{ MEDIA_URL }}` context variable in our templates. To have `{{ MEDIA_URL }}` included automatically in each template we need to do two things:
1. Make sure you have `django.core.context_processors.media` in your `TEMPLATE_CONTEXT_PROCESSORS`.
1. Make sure each view is using a [`RequestContext`](http://docs.djangoproject.com/en/dev/ref/templates/api/#id1).
Afterwards all we need to do is to specify our media URL's like this:
<img src="{{ MEDIA_URL }}img/header.jpeg" />
This will be translated to:
### Bonus
While we are at it, why not serve our `500` and `404` pages statically. When `DEBUG == True`, 500 (server error) and 404 (not found) situations are handled with special debugging views. So there's no chance to test your error pages. Add the following code, just like static serving code:
1 if settings.DEBUG:
2 urlpatterns += patterns('',
3 (r'^404/',
4 'django.views.generic.simple.' \
5 'direct_to_template',
6 {'template': '404.html'}),
7 (r'^500/',
8 'django.views.generic.simple.' \
9 'direct_to_template',
10 {'template': '500.html'}))
Now when you visit `/500/` and `/404/` on your development server you will be served a fake error page.
-----
**1**: There is an exception here. If you configured your settings correctly, development server will serve admin media.
**2**: Assuming your `settings.py` is directly inside your project directory, hence the `__file__`.
Tags: django, media, template, tutorial
This entry was posted
on Monday, May 25th, 2009 at 12:30 and is filed under Programming.
You can follow any responses to this entry through the RSS 2.0 feed.
Both comments and pings are currently closed.
what you need is to keep track of md5′s of all static files in memory, then generate false paths for each static file such as /dres/34AEFAA1785DDD/file.css – then use url redirecting to direct /dres/[regexp]/*.css to actual path. then you can use a filter to rewrite the http response header in such a way that every file you serve has a long expiry date like 10 years. This way you never send the same file to the same client again, yet when the file changes the checksum changes, and the client cache is therefore flushed. You need to be able to masterfully modify the http header (multiple fields – expires, pragme and cache-control) with your own values. Too much fuss just to serve files? Thats the way you do it.