Overriding a Widget in the Django Admin Site

Posted in Uncategorized on October 5th, 2008 by varikin

In an app I am creating in Django, I decided I want to be able to format some text. There are several ways I could do this: allow all HTML, some HTML, or some other markup syntax. I decided I don’t want HTML for security reasons, either all or just a subset. So I decided to go with another markup syntax. I am looking at both Markdown and Textile. To make this work, I want an “editor” because I will never remember all the syntax.

The big part is then getting the editor incorporated into the Django admin site. It took some work, several tries, and some pure luck but in the end I am happy with the results.

First, I chose the markItUp! editor because it looks nice, it is a jQuery plugin (I am alreay using jQuery), and it is not tied to any markup syntax. To use this editor, I needed to configure Django to use a different widget than the normal textarea widget for my model. I found this post and followed its directions. I worked great, but just didn’t feel right. Then by pure chance I found that you can specify which form to use for the ModelAdmin. To do this, I created a widget, MarkItUpWidget:

class MarkItUpWidget(forms.Textarea):
    class Media:
        js = (
            'js/jquery.js',
            'js/markitup/jquery.markitup.js',
            'js/markitup/sets/markdown/set.js',
            'js/markItUp_init.js',
        )
        css = {
            'screen': (
                'js/markitup/skins/simple/style.css',
                'js/markitup/sets/markdown/style.css',
            )
        }

The widget subclasses Textarea and includes the JavaScript and CSS files needed for MarkItUp plus a JavaScript file, markItUp_init.js, that initilizes MarkItUp:

$(document).ready(function()    {
    $('textarea').markItUp(mySettings);
});

Then a ModelForm, PostAdminForm is needed that uses the MarkItUpWidget:

class PostAdminForm(forms.ModelForm):
    raw_body = forms.CharField(widget=MarkItUpWidget())

    class Meta:
        model = Post

PostAdminForm uses the Post model and specifies the MarkItUpWidget for raw_body, a model attribute on the Post model which will holds the unprocessed markup text. The last thing is setting the form for the PostAdmin and registering it.

class PostAdmin(admin.ModelAdmin):
	form = PostAdminForm

admin.site.register(Post, PostAdmin)

After all this, here is what the admin site looks like for the Post.

MarkItUp in Django Admin

The top text area is for the input. The toolbar is added automatically by MarkItUp. The check mark on the toolbar generates a preview of the marked up text which MarkItUp automatically places in the box below.

Then the plain text (top area) is send to Django when saved. I have some server side hooks to process the save and preview, but I will get to that in another post.

There are still some issues being worked out, like the fact that the links are not working (notice the non-blue and non-underlined word “link” in the preview), but that is probably something with my server side processing.

The Django documentation tells how to do each bit of this, but doesn’t show to how put it all together, so it took a while and luck to randomly find the right parts (like setting form in PostAdmin). I am just happy there such a nice simple way to accomplish this. Also, any custom widget could be added to the admin change/add page like this, not just the markItUp! widget for text areas.  The main thing getting the custom widget class setup the way you need it.

Tags: ,