Pretty Django Error Pages

Continuing on with the simple tricks that make everyone's life a little bit better, I know a lot of people hate that Django's 500 pages don't get rendered as a RequestContext. This means that if you have context processors (like one that sets a MEDIA_URL), they don't get called. This was causing our 500 pages not only to make users sad because something broke, but knock them out of context becaue our entire design blew up.

Luckily, Django makes it incredibly simple to redefine your 500 handler in your URLConf. Most pythonistas know that import * is a bad thing, but it is standard in the Django community in your URLConf to do a from django.conf.urls.defaults import *. This has the effect of pulling in Django's default handler500 function. So if you want to override Django's default, you simply set it up like so.

from django.conf.urls.defaults import *
handler500 = 'path.to.my.sweet.views.server_error'

Then you simply define a server_error view that renders the error page with a RequestContext.

from django.shortcuts import render_to_response
from django.template import RequestContext

def server_error(request, template_name='500.html'):
    """
    500 error handler.

    Templates: `500.html`
    Context: None
    """
    return render_to_response(template_name,
        context_instance = RequestContext(request)
    )

If you're feeling extra special, you can even change the template rendered. Note that you can also do this for the 404 handler by defining a 404handler in your URLConf in the same fashion. Then you can get pretty error pages!




Comments

1 Aidas Bendoraitis says...

What's really missing in Django, in my opinion, is the error handler for "Permission Denied" error (503) which is raised when a user has no permission to perform certain action or access specific page.

Posted at 12:28 a.m. on September 24, 2009

2 布里斯班 says...

This looks much nicer than the built in error pages

Posted at 2:50 a.m. on September 24, 2009

3 Nick says...

I use Django+fastcgi via flup. Sometimes I got "Unhandled exception" produced by flup. Any ideas how I can make it pretty? :)

Posted at 4:44 a.m. on September 24, 2009

4 zgoda says...

@Nick:

You cann't, at least not in your app's code. This is handled by flup and happens before Django can do anything with request.

The template for this error page is embedded within flup's own Python code. Those brave enough can modify it there...

@original post:

The decision to not use RequestContext in error page is understandable - if 500 occurs, you can not be sure any of context processors will not raise additional errors that might hide actual stack trace. Anyway, the idea described here is very pythonic. :)

Posted at 6:56 a.m. on September 24, 2009

5 Dougal Matthews says...

"Most pythonistas know that import * is a bad thing"

Yeah, I really don't like import * and its always made me feel uneasy.

Posted at 2:43 p.m. on September 24, 2009

6 Eric Holscher says...

@Aidas: Indeed. Is there a ticket open for that? Seems like something that should at least be raised for discussion.

@zgoda: Yea, I understand the reason that it is not enabled by default. However, we have been running with this setup for a while now. I guess if you just have one site, you can hard code the URLs for your media, but since we run a lot of them, it needs to be dynamic (the whole reason we have MEDIA_URL).

I haven't noticed anything that has broken and been in the context processors or middleware, and chances are, things that break there will break all over your site.

Posted at 3:41 p.m. on September 24, 2009

7 Robert says...

Sorry for code messup, I'm not familiar with markdown:

    class SaveTraceExceptionMiddleware(object):
        """
         - add to middleware 'SaveTraceExceptionMiddleware',
        """
        def process_exception(self, request, exception):
            if not settings.DEBUG:
                try:
                    assert settings.EXC_TRACE_FNAME
                    tech_response = technical_500_response(request, *sys.exc_info())
                    error_id = datetime.now().isoformat("-").replace(":","-")
                    fname = settings.EXC_TRACE_FNAME % error_id
                    if os.path.exists(fname):
                        logger.warning("Warning: file will be overwritten %s" % fname)
                    fout = file(fname, "w")
                    fout.write(tech_response.content)
                    fout.close()
                    logger.info("Exception technical response saved in %s" % fout.name )
                except Exception, e:
                    logger.error("Error when saving exception to file: '%s' / '%s' " % ( str(sys.exc_info()), str(e), ))

Posted at 10:21 p.m. on September 24, 2009

8 Robert says...

Btw. if you do preview of the comment, and do preview again -> you won't preview it for the second time, it will be posted (Thank you for your comment). It happened to me twice.

Posted at 10:23 p.m. on September 24, 2009

9 Carl Meyer says...

This probably won't cause any problems, but why take the risk? For our projects we just have a standard, very attractive but simple 500 page that's fully self-contained (CSS and all), so it doesn't need MEDIA_URL. Works great, looks great, no risk.

Posted at 4:25 p.m. on September 25, 2009

10 Earth says...

Good article, thank you If you are interesting 2012 year, by the way, welcome to us :) http://2012earth.net

Posted at 7:44 a.m. on October 10, 2009

Comments support markdown

Comments are closed.

Comments have been close for this post.