Apologies to anyone who tried to read this post earlier in the week; I implemented an "is_public" attribute to my blog post model (so I could write articles over time without them showing on the site), but forgot to add the "is_public" check to my django feed -- which, so happens, the aggregator at djangoproject.com polls on a regular basis -- thus my in-process article showed up in the aggregator, but gave a 404 error if you tried to read it!
Sounds like a good test case... :)
Anyway, my previous posting regarding Django configuration, "localized settings in django" focused on splitting project settings into two modules: the main settings.py module and a new local_settings.py module. The purpose, of course, was to be able to modify Django settings based on the environment it was currently running on. I wasn't surprised to find that other people had come up with similar solutions to this common problem, and some were good enough to comment and share their approaches.
The purpose of this effort, at least in my regards, is to try and create Django projects/applications that aren't bound by the environment they run in. Django does a lot of work for us to decouple areas of the application, roughly following the fabled MVC design pattern: the models.py module defined in each application; the views as described by our templates; and finally the controller, which the Django developers describe as the URL dispatcher and the framework itself.
Dynamically Static
One of the issues I've run into using Django, however, is the subject of serving static media files; and by static media files I mean javascript, cascading stylesheets, images, etc. As the previous link says, Django doesn't really facilitate serving static resources in production but it does provide an "inefficient and insecure" method for development by simply adding an entry in your URLconf:
# urls.py urlpatterns = patterns( '', ... ( r'^static/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': '/home/mike/dev/static' } ), ... )
Requests to our Django host ("http://localhost:8000" in development) can now serve static media located in the /home/mike/dev/static directory via the /static root URL (e.g. "http://localhost:8000/static/images/header.jpg") while it is presumed that in production, your static media will be served by a separate server (e.g. "http://static.example.com/images/header.jpg").
So again, what's the problem? Besides that now all your templates are hard-coded to get static media from one URL and need to be changed for staging/production, I mean? Yeah, that could be a problem.
Python to the Rescue
Fortunately, Django and most of our application is Python code -- everything but templates and the static media itself -- so there are many creative solutions to our problem. I'll try to detail the approach I took with my projects; if you have a unique method for this issue, please share how you solved it below (if it's different than my own, of course).
The first thought that might come to mind when wanting to specify a media location for the project would be the settings.py module and/or the local_settings.py module. Looking through the available settings defined in these files, you'll come across two that seem perfect for the job: MEDIA_URL and MEDIA_ROOT. Although these settings seem right, be forewarned: these settings are used for the file-upload field when defining models. As I didn't want to have any conflicts in the future with settings that are already used by the framework, I decided to use two of my own: STATIC_URL and STATIC_ROOT.
We can now define in our collective settings modules where our static resources are served from:
# settings.py ... STATIC_ROOT = '' STATIC_URL = 'http://static.example.com' ... #local_settings.py ... STATIC_ROOT = '/home/mike/dev/site/static' STATIC_URL = '/static'
That's great, but how do we utilize these fancy new settings we just created in our templates? We actually have a couple options for these static media settings, both of which have pros and cons.
Templatetags
Creating a templatetag to access our settings is relatively easy, especially when we use the @register.simple_tag decorator. All you need to do is create a module in a templatetags folder for one of your apps. Here's an example:
# myapp/templatetags/myapp_tags.py from django.conf import settings from django.template import Library register = Library() @register.simple_tag def get_static_url(): return str( settings.STATIC_URL ) #get_static_url = register.simple_tag(get_static_url) #add this if you can't use decorators
Now in our templates, we can get at the setting by loading ( {% load myapp_tags %} ) and using ( {% get_static_url %} ) the templatetags we just created:
{% extends "base.html" %} {% load myapp_tags %} ... <link href="{% get_static_url %}/css/default.css" rel="stylesheet" type="text/css" media="screen" /> ...
That's it! When the template is rendered, the link will point to however STATIC_URL is defined in your settings file (e.g. "/static/css/default.css" ). The only real downside of using templatetags for outputting data like this is aesthetics -- most (but not all) templatetags are used for performing logic on variables in your templates; "for" loops, "if", "ifequal", etc... They also require the "load" tag in every template that's going to reference them, regardless of whether an "extended" template had already loaded it.
Custom Context Processors
The other method, creating a custom context processor, solves the aesthetics problem and the requirement to "load" in the template; however, it introduces its own downside as well. Context processors, to be brief, can inject whatever variables we want into the context -- what that basically means is by using a context processor, we can access our URL in our templates using the common method of accessing variables in templates, in this case: "{{static_url}}". However, they take a little bit more setup:
# mysite/context_processors.py from django.conf import settings def setting_tags( request ): return { 'static_url': settings.STATIC_URL }
We now need to tell Django about our custom context processor. Note that this setting overrides the default, so be sure to include the other context processors that are part of the core of Django:
# settings.py ... TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'mysite.context_processors.setting_tags', )
Now the downside: you need to add the special context class RequestContext to any view that will render a template with your extra context processor:
# myapp/views.py from django.shortcuts import render_to_response from django.template import RequestContext def index_view( request ): return render_to_response( "main/index.html", context_instance=RequestContext( request ) ) #return render_to_response( "main/index.html", {}, RequestContext( request ) ) #slightly shorter
Please note that generic views automatically include the RequestContext, so nothing to do there. It's a pity all views don't as well.
Automation
Now to take our setup on step further, and have our static content be served automatically from the correct place based only on our configuration (a.k.a. settings.py module). If you're just using different media servers for the static content, between production and staging, than you're already done. However, for development (say locally) you need to do a little more work:
# mysite/urls.py from django.conf.urls.defaults import * urlpatterns = patterns( '', ... ( r'^admin/', include( 'django.contrib.admin.urls' ) ), ... ) if settings.STATIC_URL[ :5 ] != 'http:': urlpatterns += patterns( '', ( r'^' + settings.STATIC_URL[ 1: ] + '/(?p<path>.*)$', 'django.views.static.serve', { 'document_root': settings.STATIC_ROOT, 'show_indexes': True } ) )
Pretty simple, no? We're basically testing how the STATIC_URL is configured; if it isn't from a different server (starts with 'http:') than we're obviously serving the media locally and use the 'django.views.static.serve' view to serve the files. Notice we tweak the value of STATIC_URL a bit so that it can be used for the regex pattern in urlpatterns (basically excluding the '/' prefix).
Between a localized settings module and dynamically serving static content, we have an extremely flexible setup. Paired with version control, we can fully develop locally (code and static resources) with only the need of, for example, an "svn update" to deploy our changes to other environments.

/blog/dynamically-serving-static-content-django/comments
Very well thought out. I like ideas that when I hear them I think "Damn! Why didn't I think of that!"
I took things yet a step further and made it so that an data in an apps '/media/' directory was automatically served up, and could have its location overridden in the server settings file.
To round things out the template tag looks like:
{% load appmedia %}
{% app_media_prefix "myapp" %}
The source code is here:
https://svn.python.org/conference/dja...
https://svn.python.org/conference/dja...
https://svn.python.org/conference/dja...
Project information:
http://us.pycon.org/TX2007/PyConTech
If you want to get really really fancy, there is a context processor which safely brings in all of django.conf.settings in a lazy way (and disallows access to sensitive information).
It still needs some work, but it allows for packaging up the media in the apps so they can be moved to different sites easier.