Distributing media with Django apps
Reusable Django apps have a sore spot right now: media.
Introduction
Django takes a hands-off approach to your media files. Your project has two settings, MEDIA_URL (the base URL where your media is located) and MEDIA_ROOT (the filesystem path where media files are stored). The only thing it does with these is put FileField files under MEDIA_ROOT. The rest — collecting the required files, serving them up somehow, and ensuring that they’re loaded from MEDIA_URL in templates — is completely up to the developer. This mostly works out great, since many sites will have a dedicated media server, and developers probably know better than Django which media files should be loaded where. It isn’t perfect, however, because there is one thing Django could help with: collecting the required files.
The problem
Some Django apps — batchadmin, for example — distribute media (CSS, images, JavaScript) that is necessary (or recommended) for the app to work. Doing this is currently ad hoc and annoying for both the app developer and the app user.
Since there is only one MEDIA_ROOT, the app’s files have to get in there somehow. How do they get there? Who knows, but the person installing the app has to do it manually. Okay, now the app should be able to access its media, right? Maybe. It depends, where did the user copy/link/move those files to, anyway? The app must now define its own settings so the user can tell it where they put the app’s media files in MEDIA_ROOT.
The solution
The approach I propose is hopefully the simplest thing that could possibly work. I think that approach is to just find all media files in the project’s INSTALLED_APPS and put them in MEDIA_ROOT. This idea has resulted in a collectmedia management command that can be run once by the user at installation or deployment time. This way, if you have apps laid out like:
app1/media/
app1/media/app1/
app1/media/app1/css/style.css
app1/media/app1/js/forms.js
app2/media/
app2/media/app2/
app2/media/app2/css/fonts.css
app2/media/app2/img/icon.png
…then running collectmedia will let the apps reference their media like so (in a template, for example):
{{ MEDIA_URL }}app1/css/style.css
{{ MEDIA_URL }}app2/css/fonts.css
Just like with reusable app templates, it should be best practice to make a subdirectory under media with the same name as the app. This way, app2 can override app1’s style by including a file with the path app2/media/app1/css/style.css. And just like with templates, when multiple apps provide a file with the same relative path, the app listed in INSTALLED_APPS first is selected.
This approach avoids having Django try to do any sort of dynamic dispatching of media files, because that would eliminate the advantage of using a media server completely independent of Django.
Speak up
If you have ideas or opinions on this matter, and especially if you want to see something like this included in Django, check out the relevant thread on the django-developers mailing list.