Now ETagging Right!
17Apr2007 [webprog]
In which I stop fighting the framework and let Django handle ETags the right way
Am now handling etags right! Django already takes care of the messy business - caching generated views and sending the appropriate etags, so if a view doesn't change, the browser shouldn't have to download it again.
macosxpeter:~ peter$ wget -S http://wintermute.com.au/
--19:44:57-- http://wintermute.com.au/
=> `index.html'
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 21112
ETag: 3290c8e891866e8b1b0884e3a3820b7e
Content-Type: text/html; charset=utf-8
Length: 21,112 (21K) [text/html]
macosxpeter:~ peter$ wget -S --header='If-None-Match: 3290c8e891866e8b1b0884e3a3820b7e' http://wintermute.com.au/
--19:45:53-- http://wintermute.com.au/
=> `index.html.1'
HTTP request sent, awaiting response...
HTTP/1.1 304 NOT MODIFIED
Content-Type: text/html; charset=utf-8
19:45:54 ERROR 304: NOT MODIFIED.
But the etags weren't doing anything before, as I was generating random references to the banner image and to album photos on every load, so the view would change. I've now written a handy little 'randoms' app instead. Now the html for each page doesn't change - it just points to /random/banner.jpg and to, eg, /random/photo.jpg?1 through ?4. Those point to ordinary django views that deliver the contents of a random banner or photo. So the etag on those image references will change, but not on all the html pages, and that should make a real difference, as there's rarely much real change on most of the pages round this site...
The Django code, for anyone interested
Create a django app in the usual manner; this one is called randoms (I tried random until I realised it kept conflicting with the standard Python library random); you can lose the models.py file as this just pulls in other models...
Note that my setup differs from the usual advised by Django's docs, in that I have the folder where all my apps sit on the Python path. What this basically means is that I don't have to keep putting my_project. before all my app calls, as hey, that violates DRY (though, true, it would've let me call the app random, so I'm not going to argue this point). So if you're using the default setup, remember to drop a my_project. into a bunch of the imports.
And of course, after creating the randoms app, you'll want to include it in your INSTALLED_APPS, and include this line in your project's urls module:
(r'^random/', include('randoms.urls')),
urls.py:
from django.conf.urls.defaults import *
urlpatterns = patterns('randoms.views',
(r'^banner.jpg$', 'banner'),
(r'^album-(?P<album_id>\d+).jpg$', 'album'),
(r'^photo.jpg$', 'photo'),
)
views.py:
from django.conf import settings
from django.http import HttpResponse
import os, random
from photos.models import Photo,Album
def banner(request):
#return a random from the banners folder
banner_path = settings.MEDIA_ROOT + 'banners'
dir_list = os.listdir(banner_path)
file_list = []
for file in dir_list:
index = file.rindex(".")
if(file[index:] == '.jpg'):
file_list.extend([file])
rand = random.choice(file_list)
return HttpResponse(open(banner_path + '/' + rand,'rb',0).read(),'image/jpeg')
def album(request,album_id):
#return a random thumb from the given album
photos = Album(album_id).photos.values('filename')
photo = random.choice(photos)['filename']
photo_path = settings.PHOTO_ROOT + 'thumbs/' + photo
return HttpResponse(open(photo_path,'rb',0).read(), 'image/jpeg')
def photo(request):
#return a random thumb
photos = Photo.objects.values('filename')
photo = random.choice(photos)['filename']
photo_path = settings.PHOTO_ROOT + 'thumbs/' + photo
return HttpResponse(open(photo_path,'rb',0).read(), 'image/jpeg')
I've got a lot in here; obviously you can pick and choose between the various views, but there are probably a few useful bits in here.
def banner:
This view, called with /random/banner.jpg, returns the contents of a random image stored in a path of your choosing; I just created a banners folder under the MEDIA_ROOT, though it could easily live outside the webroot. It loops through the folder, looking for .jpgs, picks one at random, reads it in (open(path).read()) and outputs it... Django's HttpResponse is usefully flexible, passing 'image/jpeg' to it means it'll serve the response up with the appropriate mimetype (so the browser has no way of knowing the image was generated).
def album:
This one hooks into my Album model. The idea is that it returns a random image from the given album, so I call this with /random/album-N.jpg, where N is an album's id. (I haven't put the necessary checks in to make sure that N exists, but there may be circumstances where you'd want to do that.) This view grabs a list of files associated with the given album (photos is a ManyToManyField), and returns one at random. The only weird thing here is settings.PHOTO_ROOT, which is a setting I use on this site for my photos app - I use it because I keep a number of photo files outside of the webroot, so the MEDIA_ROOT setting wasn't enough.
Update
Made a little tweak to make the code work on my Windows box; the open call needs a switch to tell it not to buffer ('rb',0); cf random on windows
« The Roots, Live at the Enmore :: Photos with location clouds »
Related [webprog]
- None of these seem to get in the way (17May2010)
- IE6 and 7 on OSX (20Sep2008)
In which I run IE 6 and 7 on OSX; the entire process using free software - Random on Windows (15May2007)
In which I am baffled by the mystery that is Windows, and once more wooed by the ease with which one can make Python work, even there - Now ETagging Right! (17Apr2007)
In which I stop fighting the framework and let Django handle ETags the right way - Semantics and Style :: Markup for Dialogue (30Dec2006)
- Winter with Django (02Dec2006)
- Drop that double-u-double-u-double-u-dot (08Nov2005)
- Blog/Search (07Oct2005)
- Converting Relative to Absolute links in PHP (preg_replace) (21Sep2005)
- Think You’re Fighting Spam? (14Sep2005)
- BBC backstage access (12May2005)
- funky google trick (28Sep2004)
- The name of this class, ‘abc’, conflicts with the name of another class that was loaded, ‘abc’ (28Jun2004)
- The Altar Of Reason (10Jun2004)
- Domain.com.au Feedback (17May2004)
- aligned with evil (11May2004)
- flash gallery thingy (08Apr2004)