Ajax Django Comments With jQuery

原文地址: http://www.adoleo.com/blog/2009/feb/16/ajax-django-comments-jquery/

 

I haven't posted here in awhile because of quite a few things going on in my personal and professional life lately. Things are finally beginning to calm down a bit, however, so I'm planning to return to the blogging keyboard on a regular basis going forward. The first thing I'd like to share with the Django community is my basic implementation of Ajax comments using the built-in Django comment system.

 

This is certainly not the only way to do this, so if you've got ideas or comments please share them at the bottom of this post. This is how I've done it on a few sites so far, and it's worked well for me.

 

To enable the posting of comments through Ajax, I utilized the jQuery JavaScript toolkit. You can still follow this post if you're using another toolkit, however, because the principles should remain the same.

Creating a Custom Ajax Comment Post View

The first thing to do is create a new subfolder under your project root called "ajax". Within the new ajax folder, create three files: __init__.py so that Python recognizes the folder as a module, urls.py for all Ajax related urls going forward, and views.py for all Ajax related views going forward.

Ajax Urls

In your ajax/urls.py file, add the following code:

 

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.ajax.views',
    url(r'^comment/post/$',
        view='ajax_comment_post',
        name='ajax_comment_post'),
)

 

This establishes a url for the comment post view. After saving that file, go to your main urls.py file in your root project folder. Add the line below to hook your new ajax/urls.py file into your main urls:

 

(r'^ajax/', include('myproject.ajax.urls')),

 

 

Customizing the Standard Comment View

With that in place, it's time to move on to the view in ajax/views.py. Go to Django's code browser and copy the original view from /django/trunk/django/contrib/comments/views/comments.py. Paste the text into your ajax/views.py file, and get ready to slice it up for JSON serialization. Responses encoded as JSON objects can be easily parsed by most JavaScript toolkits. You will return two objects to the Ajax process that calls the page - "status" and "error". Status is set to "success", "error", or "debug". Error is set to a brief message describing an error. To use JSON serialization, add the following import line to the top of your view file:

 

from django.utils import simplejson

 

 

First, remove the CommentPostBadRequest method. This method renders error messages to the debug 400-error view. Since this page is going to be called through asynchronous JavaScript, it isn't particularly helpful to return a debug page because it wouldn't be seen. Then, go through the view and locate each call to CommentPostBadRequest. Replace each instance with code similar to the following:

 

if ctype is None or object_pk is None:
            error = "Missing content_type or object_pk field." 
            status = simplejson.dumps({'status': 'debug', 'error': error})
            return http.HttpResponseServerError(status, mimetype="application/json")

 

Set the status as "debug" because this isn't the type of error that you typically want to show up in production. This is not a user error, it's an error related to the code itself. In production, this should be displayed to the user as something similar to "The server has encountered an error, please contact an administrator."

 

The next section to alter is the block that deals with form errors. These are errors that you want displayed to the user because they indicate issues with the form that they have the ability to correct. In order to handle this, change the error handling block to join together any errors and return them in the "error" JSON object.

 

if form.errors or preview:
    error = ""
    for e in form.errors:
        error += "Error in the %s field: %s" % (e, form.errors[e])
    status = simplejson.dumps({'status': 'error', 'error': error})
    return http.HttpResponse(status, mimetype="application/json")

 

The last change to make to the view is to return a "success" status if the comment is successfully created. To implement that, change the return line underneath the comment.save block to look like this:

 

status = simplejson.dumps({'status': "success"})
return http.HttpResponse(status, mimetype="application/json")

 

Implementing the Client-Side Interaction with jQuery

With the server-side code now complete, it's time to move on to the client-side JavaScript code. Create a postComment.js script in your static media directory, and add a line to the appropriate template to make sure the script is loaded on any page that allows the posting of a comment.

 

To start the script off, add a $(document).ready() section:

 

$(document).ready(function() {
    debug = true;

    $('div.comment-form form').submit(function() {

        // The rest of the function will go here. . .

        return false;
    });
});

 

 

This block hijacks the form's submit event, allowing you to alter the behaviour through JavaScript. The debug = true lets the script know that you're currently in development and it should display the "debug" error messages. When you deploy, make sure to change that to false. Using return false at the end of the function ensures that the event will not continue to propagate and send the user through the standard comment post process.

 

Inside your submit function, the first thing to do is make sure any errors from a previous submit attempt are cleared away. I wrap my errors in a div with a class of "comment-error", so this line makes sure all "comment-error" elements are removed. You can alter this to suite your template as needed.

 

$('div.comment-error').remove();

 

 

Next up, read all of the comment form fields into variables.

 

name = $('#id_name').val();
email = $('#id_email').val();
url = $('#id_url').val();
comment = $('#id_comment').val();
content_type = $('#id_content_type').val();
object_pk = $('#id_object_pk').val();
timestamp = $('#id_timestamp').val();
security_hash = $('#id_security_hash').val();
honeypot = $('#id_honeypot').val();

 

 

Then, use jQuery's post function to hit the /ajax/comment/post/ view that you created earlier, making sure to include all of the values you read into variables above.

 

$.post('/ajax/comment/post/', {
    name: name,
    email: email,
    url: url,
    comment: comment,
    content_type: content_type,
    object_pk: object_pk,
    timestamp: timestamp,
    security_hash: security_hash,
    honeypot: honeypot,
}, function(data) {

    // The success or failure check will go here. . .

}, "json");

 

 

 

jQuery's post function takes the URL as the first argument, a list of key/value pairs for the POST data next, then a callback function that is triggered upon receiving a response, and finally a string indicating the type of encoding to interpret the response data with.

 

In the callback function, including the object that is created automatically from the response data. In the code above, I called it data. It operates as a typical JavaScript object. Attributes of the object correspond to the values that we encoded in the view above - data.status and data.error. In your callback function, check for success and then take a few actions depending on the status of the post:

 

if (data.status == "success") {
    // If the post was a success, disable the Post button to
    // prevent accidental duplication.
    $('input.submit-post').attr('disabled', 'disabled');

    // If this is the first comment, I add the "## comment(s) so far"
    // banner that I use to introduce the comments section.
    if ($('div#comments').children().length == 0) {
        $('div#comments').prepend(
            '<h2 class="comment-hd">1 comment so far:</h2>'
        )
    }

    // Here I build the HTML to use for the comment.  I set the
    // style to "display:none" so that I can fade it in with an
    // animation.
    comment_html = '<div class="comment" style="display: none;">' +
        '<div class="comment-body"><p>' + comment +
        '</p></div><div class="clear"></div>' +
        '<p class="posted-by">Posted by <a href="' + url +
        '">' + name + '</a> 0 minutes ago.</p></div>'

    // Then I add the comment at the bottom of the comments section
    // and fade it in.
    $('#comments').append(comment_html);
    $('div.comment:last').show('slow');

    // I hide the comment form to prevent duplication, and
    // replace it with a success message for the user.
    $('.comment-form').hide()
        .html('<p class="comment-thanks">Comment successfully' +
              ' posted. Thank you!</p>')
        .show(2000);
} else if (data.status == "debug") {
    if (debug) {
        // If the site is currently in development, list the debug
        // errors.
        $('div.comment-form').before('<div class="comment-error">' +
            data.error + '</div>');
    } else {
        // Otherwise, display a generic server error message.
        $('div.comment-form').before('<div class="comment-error">' +
            'There has been an internal error with the server. ' +
            'Please contact a site administrator.</div>');
    }
} else {
    // If there were errors with the form, I add them to the
    // page above my comment form with a "comment-error" div.
    $('div.comment-form').before('<div class="comment-error">' +
        data.error + '</div>');
}

 

 

 

And with that, you've completed a basic implementation of Ajax comments using Django's built-in comments. This was just a quick overview of the core concepts, and there is a lot more that you can do with this. For example, I omitted previewing from this post for simplicity but you could easily implement a quick preview process that checks the form for errors but doesn't actually post the comment. Also, for the Adoleo blog, I used a jQuery md5 plugin to create hashes for Gravatar URLs, so that the user's picture is displayed next to their comment.

 

I hope my first post after my brief hiatus has been helpful! Make sure to check back soon because I'll be posting more in the weeks ahead.

你可能感兴趣的:(JavaScript,jquery,json,Ajax,django)