Login

Git media cache busting tag

Author:
adamlofts
Posted:
November 18, 2009
Language:
Python
Version:
1.1
Score:
3 (after 3 ratings)

This tag appends the current git revision as a GET parameter to a media files so the web server can set an expires header far in the future. Your project must be structured such that MEDIA_ROOT/../.git exists.

Usage: <link rel="stylesheet" type="text/css" href="{% media myapp/css/base.css %}">

 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
from os import path

from django import template
from django.conf import settings

register = template.Library()

@register.tag(name="git_head_revision")
def do_git_head_revision(parser, token):
    return GitHeadRevisionTag()

class GitHeadRevisionTag(template.Node):
    
    head = None
    
    @staticmethod
    def _get_head():
        git_dir = path.normpath(path.join(settings.MEDIA_ROOT, '..', '.git'))
        
        # Read the HEAD ref
        fhead = open(path.join(git_dir, 'HEAD'), 'r')
        ref_name = fhead.readline().split(" ")[1].strip()
        fhead.close()
        
        # Read the commit id
        fref = open(path.join(git_dir, ref_name), 'r')
        ref = fref.readline().strip()
        fref.close()
        
        return unicode(ref)
    
    @staticmethod
    def get_head_cached():
        if not GitHeadRevisionTag.head:
            GitHeadRevisionTag.head = GitHeadRevisionTag._get_head()
        return GitHeadRevisionTag.head

    def render(self, context):
        return GitHeadRevisionTag.get_head_cached()

@register.tag(name="media")
def do_media(parser, token):
    try:
        tag_name, path = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag must one argument" % token.contents.split()[0]
    return MediaTag(path)

class MediaTag(template.Node):
    
    def __init__(self, path):
        self.path = path
    
    def render(self, context):
        head = GitHeadRevisionTag.get_head_cached()
        fullpath = settings.MEDIA_URL + self.path + '?' + head
        return fullpath

More like this

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

Comments

pbx (on November 18, 2009):

Cool. How about a usage example? (I.e. template code.)

#

adamlofts (on November 19, 2009):

The tag will expand a line like this:

<link rel="stylesheet" type="text/css" href="{% media myapp/css/base.css %}">

into a line like:

<link rel="stylesheet" type="text/css" href="/path/to/media/root/myapp/css/base.css?a76s8d7a6s87d6s8a76ds">

#

mk (on November 19, 2009):

The same thing, a little bit shorter (depends on git being installed and executable by the webserver):

@register.simple_tag
def media_url(path):
    return os.path.join(
        settings.MEDIA_URL,
        path) + '?v=' + media_url.version

media_url.version = subprocess.Popen('git rev-parse --short HEAD',
    cwd=settings.MEDIA_ROOT,
    shell=True,
    stdout=subprocess.PIPE).communicate()[0]

#

adamlofts (on November 19, 2009):

@mk:

Thanks for the suggestion. Whats nice about your soln is that it doesn't rely on the project structure to get the .git folder. I guess my tag could be changed to just look for a .git folder up the directory hierarchy just as the git binary does.

As you mention, spawning a git process does have some shortcomings.

What I didn't mention in the description is the git_head_revision tag which is good for putting a version number on your website. e.g.

<p>Server running app version {% git_head_revision %}</p>

#

kylefox (on November 20, 2009):

Why not just add {{ git_revision }} with a context processor? Seems less obstrusive to simply write ?v={{ git_revision }} in your templates, plus then you could use it elsewhere in your templates (like adding the revision to your site footer for superusers).

Anyway, cool idea. I think I'll adapt this concept some of my projects.

#

Please login first before commenting.