DRY Ajax Comments

Back in February, I posted an entry about adding Ajax functionality to the built-in comments app. I always intended to go back and improve this method, but didn't get the chance until now. Yesterday I decided to take another look, and I found a much better way of achieving this functionality without violating the DRY principal like I did originally. I've posted the code on Bitbucket as django-ajaxcomments, and I'm releasing it under the same BSD license that Django uses.

The Problem With Copy & Paste

In my original post, I copied the django.contrib.comments.views.comments.post_comment view and then modified certain pieces of it to return JSON instead so that the jQuery callback function could interpret the results. The problem with this is that the post_comment view has changed since then, and it will continue to change. If I continued to maintain the project as it was, I would have to watch post_comments for changes and update my project accordingly. If I missed an important change, then my project would instantly become unusable with the latest version of Django. This goes against DRY, one of the core principals that Django written with.

Decorators to the Rescue

One of the great features of the Python programming language is its decorator syntax, which allows a programmer to wrap a function around another function in order to enhance the function without changing its original source code. Since Django views are Python functions, they can be decorated like any other Python function.

Using this feature I was able to intercept any calls that the post_comment view made to its instance of the render_to_response or next_redirect functions. My wrapper checks to see if the request was made through Ajax (using Django 1.0's HttpRequest.is_ajax() method) and encodes the needed details of the response in JSON before returning it back to the Javascript function that made the request. If the request isn't done through Ajax, then it merely triggers the original function.

Including the Current Request Object

In order to make this decorator work, I needed to provide it with the current HttpRequest object each time it is called. To do this, I created another decorator to wrap the post_comment view itself. This decorator re-wraps the instances of render_to_response and next_redirect with the current request object each time the post_comment view is called.

Putting it all Together

To finish up, I altered my original jQuery function to interpret errors from the JSON response and highlight the errors inline on the form. I added my decorators and the necessary imports to an ajaxcomments/utils.py file, and then imported this file from a blank ajaxcomments/models.py file so that it would be invoked when Django loads installed applications.

To take a look at the project's source code, and to get instructions on how to install it within your own project, head to the Bitbucket page and take a look at my wiki entry. If you have problems, please submit issues on Bitbucket and I'd be glad to help out!

14 comments so far:

Great ! Awesome release :) I am implementing now on my site :) Thanks :D

Posted by Mario César 4 months, 2 weeks ago.

This is a great project. The only thing I changed was to use the jquery.form which provides a little overhead for you when dealing with Ajax forms. Although, some might argue the dependency isn't worth the code saved.

This method of updating the comments seems much less prone to break in the future.

Posted by Mike 4 months, 2 weeks ago.

Thanks for the kind words! I appreciate the comments. If anyone has ideas on how to enhance, please feel free to fork it on Bitbucket and send me a pull request when you're done. I'd be happy to integrate contributions.

Thanks!

Posted by Brandon Konkle 4 months, 2 weeks ago.

Really cool, i had it running within 15 minutes - Good work! I only would propose to make the post_comment.js a bit more modular/general (store selectors in variables for example), so it is easier to adjust.

Posted by Michael 4 months, 2 weeks ago.

Michael, thanks for the kind words! Making the jQuery script more modular is a great idea, one that I actually started to work on but backed out of because I didn't have time at the moment. If you'd like to take a crack at it, I'd be glad to merge the changes into the master branch.

Posted by Brandon Konkle 4 months, 2 weeks ago.

Wow, this looks awesome!

Would it make sense to render the comment with a template and send it down with the JSON response, rather than building it in post-comment.js? In the spirit of DRY, this would allow you to have your comment template in one place rather than once as a Django template, and once in Javascript. I may start a fork this weekend to investigate.

Posted by John 4 months, 1 week ago.

John, that's a great idea! I'd be glad to integrate it if you're able to set it up in a fork. Thanks!

Posted by Brandon Konkle 4 months, 1 week ago.

Is this an example of AjaxComments?

Posted by Namenswitze 4 months ago.

It seems so :)

Posted by Namenswitze 4 months ago.

Thank u for this great article. Also when submitting this for from your site. Validation errors dublicating each time when i press post button, i think u need to clear divs for field validation errors before put data in it

Posted by Space 4 months ago.

Space - thanks for the heads up! John Groszko fixed that in his fork, and I should be able to merge his changes into the master branch soon. Keep the critique coming, it's much appreciated!

Posted by Brandon Konkle 4 months ago.

WRT to the import in models.py I think it's cleaner to just register the decorator in init.py

Posted by srn 1 month, 1 week ago.

"This field is required" not cleared after successful post... ff3.6

Posted by test 1 week, 6 days ago.

for fast fix add this block after "var media = args.media;"

$('#comment-form ul.errorlist').each(function() {
  this.parentNode.removeChild(this);
});

Posted by test 1 week, 6 days ago.

Post a comment:

Feel free to use the Markdown syntax in your comment. (click for a cheat sheet)
# Header 1 #
## Header 2 ##
### Header 3 ###

Here is a Markdown link to [Warped](http://warpedvisions.org).

Now some inline markup like _italics_,  **bold**, and `code()`.

![picture alt](/images/photo.jpeg "Title is optional")

> Blockquotes are like quoted text in email replies
>> And, they can be nested

* Bullet lists are easy too

1. A numbered list
2. Which is numbered
3. With periods and a space

And now some code:

    // Code is just text indented by 4 spaces
    which(is_easy) to_remember();

A horizontal rule:

* * * *
Special thanks to Warpedvisions.org for the cheat sheet.

Related tags: ajax, comments, javascript, jquery