Login

Serve static media and indexes from app directories [Python2.5, Development only]

Author:
adamlofts
Posted:
August 16, 2008
Language:
Python
Version:
.96
Score:
1 (after 1 ratings)

This view serves static media and directory indexes for a django application. It should only be used in development, media should be provided directly by a web server in production.

This view assumes a django application stores its media in app/media (which is very common) and the file is referred to in the templates by the last part of a django app path. e.g. As in django.contrib.admin -> 'admin'.

First we check if the media is a request in an application directory; if so we attempt to serve it from there. Then we attempt to provide the document from the document_root parameter (if provided).

To use this view you should add something like the following to urls.py: if settings.DEBUG: urlpatterns += (r'^media/(?P<path>.*)$', 'site.media.serve_apps', {'document_root' : settings.MEDIA_ROOT}) You can then have the admin media files served by setting ADMIN_MEDIA_PREFIX = '/media/admin/'

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import os
import mimetypes
from django.conf import settings
from django.shortcuts import Http404
from django.http import HttpResponse
from django.views.static import directory_index

from runpy import run_module

def serve_apps(request, path, document_root=None):
    """
    This view serves static media and directory indexes for a django application. It should
    only be used in development, media should be provided directly by a web server in production.
    
    This view assumes a django application stores its media in app/media (which is very common) and
    the file is referred to in the templates by the last part of a django app path. e.g. As in
    django.contrib.admin -> 'admin'.
    
    First we check if the media is a request in an application directory; if so we attempt to serve
    it from there. Then we attempt to provide the document from the document_root parameter (if
    provided).

    To use this view you should add something like the following to urls.py:
    if settings.DEBUG:
        urlpatterns += (r'^media/(?P<path>.*)$', 'site.media.serve_apps', {'document_root' : settings.MEDIA_ROOT})
        
    You can then have the admin media files served by setting
    ADMIN_MEDIA_PREFIX = '/media/admin/'
    """
    def find_abspath(path, document_root):
        pathsplit = path.split(os.sep)
        
        # Look for the file in installed apps
        for app in settings.INSTALLED_APPS:
            # Split of the last part of the app name; e.g. 'django.contrib.admin' -> 'admin'
            app_short_name = app.rpartition('.')[2]
            if app_short_name == pathsplit[0]:
                # Work out the directory in which this module resides. This works better than using
                # mod = __import__(app)
                mod = run_module(app)
                moddir = os.path.dirname(mod['__file__'])
                
                return os.path.join(moddir, 'media', *pathsplit[1:])
        
        # Look for the file in document_root
        if document_root:
            return os.path.join(document_root, path)
        
        return None        
    
    abspath = find_abspath(path, document_root) or ''
    if not os.path.exists(abspath):
        raise Http404("No media found for path %s (tried '%s')" % (path, abspath))
    if os.path.isdir(abspath):
        # To make the template work a directory must have a trailing slash in the url
        # The one exception is when path == /
        if not path.endswith('/') and path:
            raise Http404("This path is a directory. Add a trailing slash to view index")
        return directory_index(path[:-1], abspath)
    
    mimetype = mimetypes.guess_type(abspath)[0] or 'application/octet-stream'
    contents = open(abspath, 'rb').read()
    response = HttpResponse(contents, mimetype=mimetype)
    response["Content-Length"] = len(contents)
    return response

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

antmangaka (on April 7, 2009):

hello, I get an internal server error 'str' object has no attribute 'resolve'

#

Goldan (on May 19, 2009):

Thank you for a great snippet. It saved hours for me)

To make it work for me, I had to make some corrections (maybe the reason is that I'm on Windows):

  1. line 31: replace 'os.sep' with '"/"'. Correct me if I'm wrong, but since 'path' is taken from url, it always contains forward slashes.

  2. line 40: replace 'run_module' with '__import__'. The former just doesn't work for me (maybe because of Windows again)

  3. line 41: replace all with 'moddir = mod.'__path__[0]'. This follows from the previous correction.

I've also made some adjustments to the script according to my pattern of naming media folders. I store media in app/media/media_type/app/ folders, e.g. js files in app/media/js/app/ etc. If someone uses the same pattern and needs these adjustments, feel free to ask me.

#

Please login first before commenting.