Prettyprinted

Django, Python and Drunken Ramblings

Archive for the ‘captcha’ tag

Subclassing Django’s CommentForm

without comments

Django comes with batteries included and a very powerful and useful battery is the django.contrib.comments framework. However, to make it in time for the long awaited 1.0 release the comments framework was released without customization hooks. See #8630 for more details.

The long story in a short sentence is that you cannot subclass or customize CommentForm in todays version without getting your hands dirty.

I recently enabled Captcha verification to comments on this site to prevent spammers from filling my database with non-public comments. Tor Brede Vekterli has written about how to do this by using signals, but for technical reasons I’d rather not go down that road. So I landed on the next possible strategy, namely subclassing CommentForm.

My subclass is a standard Django form:

class CaptchaCommentForm(CommentForm):
    captcha = forms.CharField(max_length=20, label='Enter this word')

    def clean_captcha(self):
        # verify self.cleaned_data['captcha'] here, details omitted,
        # raise forms.ValidationError if verification fails

Since I wanted to control the way this field is displayed by adding an image I also edited my form template. In my comments/form.html template I added a check to include the captcha image:

{% for field in form %}
    {% ifequal field.name "captcha" %}
        do whatever it takes to display the custom field.
    {% else %}
        display regular comment form field
    {% endifequal %}
{% endfor %}

As I mentioned above the comments framework is not really designed to be extended or customized in this way. To make the framework use my CaptchaCommentForm instead of its own CommentForm I resorted to monkeypatching!

The framework uses a single function, comments.get_form, whenever it needs a fresh CommentForm instance. What I wanted is for that function to return my CaptchaCommentForm instead. Here is what I did to my urls.py:

from django.contrib import comments
from forms import CaptchaCommentForm

def override_get_form():
    return CaptchaCommentForm()
comments.get_form = override_get_form

Thanks to the dynamic features of Python we can simply replace the original function with our own that returns the correct instance. Not pretty, but it works.

Written by steingrd

November 24th, 2008 at 8:32 pm

Posted in django

Tagged with , ,