Login

Caching XHTML render_to_response

Author:
smoonen
Posted:
July 23, 2008
Language:
Python
Version:
.96
Score:
1 (after 1 ratings)

This code improves on Django's render_to_response shortcut function in the following ways:

  1. If the caller does not provide a MIME type, and the caller is passing a RequestContext, it interrogates the request to determine if the HTTP client supports application/xhtml+xml encoding. If so, it sets the request type to application/xhtml+xml to override the HttpRequest class's default mime type of text/html.
  2. It caches parsed templates in its own cache to improve performance.

If you aren't using XHTML in your templates, you may choose to comment out the code that tests for and sets the application/xhtml+xml MIME type.

I place this code in a file named "util.py" in my application. In my views, I write "from app.util import render_to_response" where I used to write "from django.shortcuts import render_to_response".

Note that the caching functionality provided by this code means that you will need to recycle your Django instance when you make template changes. Instructions for doing this depend on how you have deployed your Django application.

 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
import django.shortcuts
from django.http import HttpResponse
from django.template import loader
from django.template.context import Context

template_cache = { }
def render_to_response(*args, **kwargs) :
  """Implement two changes relative to django.shortcuts.render_to_response:
       1) Use content-type 'application/xhtml+xml' if supported by the client.
       2) Cache the compiled template.
  """

  # Set content type to application/xhtml+xml if conditions allow.
  if not kwargs.has_key('mimetype') and \
     kwargs.has_key('context_instance') and \
     kwargs['context_instance'].has_key('request') and \
     kwargs['context_instance']['request'].META.has_key('HTTP_ACCEPT') and \
     'application/xhtml+xml' in \
       kwargs['context_instance']['request'].META['HTTP_ACCEPT'].split(',') :
    kwargs['mimetype'] = 'application/xhtml+xml'

  # Load template (use cache if possible).
  template_path = args[0]
  if template_cache.has_key(template_path) :
    template = template_cache[template_path]
  else :
    template = loader.get_template(template_path)
    template_cache[template_path] = template

  # Render template using provided context.
  if kwargs.has_key('context_instance') :
    context_instance = kwargs['context_instance']
  else :
    context_instance = Context()

  if len(args) > 1 :
    context_instance.update(args[1])

  rendering = template.render(context_instance)

  # Return HttpResponse using rendered template.
  httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
  return HttpResponse(rendering, **httpresponse_kwargs)

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

adamlofts (on July 23, 2008):

Hmmm.. Templates are already precompiled by django. I don't think this will make the code any faster. Have you benchmarked it?

#

smoonen (on July 23, 2008):

Hi adam. From my reading of the code in django.template.loader and django.template.loaders.filesystem, it seems to me that template files are loaded from disk on each request. So I think that my code is saving both the file load and also the template compile on every subsequent request.

Can you point me to something that shows otherwise? I certainly don't want to add unnecessary overhead! Thanks.

In my particular case this code did yield some performance improvement. I have much more significant bottlenecks still to track down, though.

#

smoonen (on July 23, 2008):

Ok, here are some more formal results. I wrote two scripts, test.py:

from django.shortcuts import render_to_response
for i in range(1000) :
  render_to_response('myapp/index.html')

and test2.py:

from myapp.util import render_to_response
for i in range(1000) :
  render_to_response('myapp/index.html')

From the command line:

$ time python test.py

real    0m27.097s
user    0m26.886s
sys     0m0.212s
$ time python test2.py

real    0m22.528s
user    0m22.329s
sys     0m0.200s

#

adamlofts (on July 24, 2008):

Ah! Your right that the template loaders do attempt to open the template name every time. One might hope that the kernel will have them cached once its warmed up a bit; but you're right (and your test demonstrates) that there is a performance increase.

#

Please login first before commenting.