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 syntaxI 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 siteIt 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 modelI found this post and followed its directions. I worked great, but just didn’t feel rightThen 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.
The top text area is for the inputThe 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 savedI 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: Django, markitup
[...] my last post, I showed a way to override a widget on the change/add page in the Django admin site. Now I will [...]
[...] Fictitious Nonsense » Overriding a Widget in the Django Admin Site (tags: programming django widget) [...]
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
Thanks a lot, very helpful tip. Keep on.
Alfredo
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?
@Rick, yes, the inner class Media is for Django widgets. It includes those things on the page when rendered.
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?
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.
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? :)
Rick, you mean my very next post, http://www.fictitiousnonsense.com/archives/33?
oh thanks :)
??????, ?? ??? ??????, ????? ?????? ? ?????? ?????, ??? ??? ????????????.
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 :-).
To follow, without halt, one aim: There’s the secret of success.