Overriding a Widget in the Django Admin Site

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: ,

15 Responses to “Overriding a Widget in the Django Admin Site”

  1. Fictitious Nonsense » Overriding a Widget in the Django Admin Site, part 2 Says:

    [...] my last post, I showed a way to override a widget on the change/add page in the Django admin site. Now I will [...]

  2. roytang / weblog » Blog Archive » links for 2008-11-25 Says:

    [...] Fictitious Nonsense » Overriding a Widget in the Django Admin Site (tags: programming django widget) [...]

  3. lifewithryan Says:

    I really like that solution…slick as hell and good to know its not that difficult. I’ve veered away from doing too much to change admin around…dunno why though, i like this approach

  4. Alfredo Di Napoli Says:

    Thanks a lot, very helpful tip. Keep on.
    Alfredo

  5. rick Says:

    I don’t understand the inner Media class. Is that a Django thing? Will that automatically insert the javascript and css files into the template for you?

  6. varikin Says:

    @Rick, yes, the inner class Media is for Django widgets. It includes those things on the page when rendered.

  7. Jesús Gómez Says:

    Hello. Very useful.

    I wanted to point that i had a little issue with this method, and is that i couldn’t set the raw_html field as an optional field in the admin, and i had to override formfield_for_dbfield as explained in the post you followed instead of defining the post arg in the PostAdmin.

    I wold like to rescue the form way so: any idea why this happend?

  8. Ed Says:

    Great tutorial thank you!

    I have one problem: the preview window (when I hit the checkmark in the markitup bar) just renders exactly what is written in the input, not parsing it to HTML. Any ideas? I’m using textile instead of markdown, but that shouldn’t matter.

  9. rick Says:

    Thanks, varikin.

    Like Ed, I’m having the problem of the preview not working.

    I found this:

    http://expressionengine.com/forums/viewthread/82013/P18/#425444

    But I’m having a hard time how to translate that into Django.

    I noticed your post says “I have some server side hooks to process the save and preview, but I will get to that in another post.”

    So any idea when we might see that second post? :)

  10. varikin Says:

    Rick, you mean my very next post, http://www.fictitiousnonsense.com/archives/33?

  11. rick Says:

    oh thanks :)

  12. Ben Tappin Says:

    Thank you! Just what I needed.

    Unfortunately this overwrites the existing attrs for the widget. I’m sure there’s a workaround but I don’t need it right now :-).

  13. Dwight Hansen Says:

    Thanks for this post. I am new at python and this is a big help.

  14. David Peers Says:

    This blog helped me somewhat in narrowing down some issues with the latest release, Why do they always seem to leave out vital documentation when they upgrade? It may be trivial to them but not for us! I’m sure i’m not alone.

  15. kamran Says:

    Thanks it is very helpful