Hunter Ford Hunter Ford

Django Messaging for AJAX Calls Using Jquery

The messaging contrib app for Django has always been been tied to a user, which has prevented me from using it in any of my apps. Now that the Django 1.2 Alpha has been released, I've been able to play with it, and feel good about using it. My sites usually have a mix of AJAX and traditional HTTP requests. So figuring out a good solution to handle messages for the AJAX requests, while maintaining consistency in interacting with the API was important to me. Here's my solution for handling messages in AJAX requests.

First we need a middleware that will detect if the request is an AJAX request. I always use JSON, but if you mix the data types, you might have to add some additional logic here. The middleware adds to the JSON any messages that we might have added in the view.

import simplejson as json

from django.contrib import messages

class AjaxMessaging(object):
    def process_response(self, request, response):
        if request.is_ajax():
            if response['Content-Type'] in ["application/javascript", "application/json"]:
                try:
                    content = json.loads(response.content)
                except ValueError:
                    return response

                django_messages = []

                for message in messages.get_messages(request):
                    django_messages.append({
                        "level": message.level,
                        "message": message.message,
                        "extra_tags": message.tags,
                    })

                content['django_messages'] = django_messages

                response.content = json.dumps(content)
        return response

Make sure to add the new middleware to settings.py.

'apps.main.middleware.AjaxMessaging'

So if we have a view that responds to an AJAX request, we can add a message the same way we would for a traditional HTTP response:

if success:
    messages.success(request, "The object has been modified.")
else:
    messages.error(request, "The object was not modified.")

We need a place to put the messages in our template:

<ul id="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
    {% endfor %}
</ul>

We'll be using a global ajaxComplete handler, so we'll have to use a JSON parser. Simply include it with the rest of our javascript. jquery.json.js

And finally the handler, which simply loops through all the Django messages, and fades them out after three seconds.

function addMessage(text, extra_tags) {
    var message = $('<li class="'+extra_tags+'">'+text+'</li>').hide();
    $("#messages").append(message);
    message.fadeIn(500);

    setTimeout(function() {
        message.fadeOut(500, function() {
            message.remove();
        });
    }, 3000);
}

$(document).ready(function() {
    $('#messages').ajaxComplete(function(e, xhr, settings) {
        var contentType = xhr.getResponseHeader("Content-Type");

        if (contentType == "application/javascript" || contentType == "application/json") {
            var json = $.evalJSON(xhr.responseText);

            $.each(json.django_messages, function (i, item) {
                addMessage(item.message, item.extra_tags);
            });
        }
    }).ajaxError(function(e, xhr, settings, exception) {
        addMessage("There was an error processing your request, please try again.", "error");
    });

Comments

  • Russell McLoughlin

    Works great, thank you! In JQuery v1.7.2 $.evalJSON doesn't seem to be defined. I replaced with $.parseJSON and all was well.
  • Fabio Masini

    Great post! Anyway, what about using custom http header, instead of injecting json code to response? In this way, you could have messages for json array (with your code if an ajax call returns an array, you get an exception)
  • Danny Adair

    Good stuff! I'm using jgrowl instead of manually appending li's.
  • Mauro

    This is very cool, i will try it for sure, thanks!