Overriding a Widget in the Django Admin Site, part 2

Posted in Uncategorized on October 9th, 2008 by varikin

In my last post, I showed a way to override a widget on the change/add page in the Django admin site. Now I will show how to process the marked up text.

First, here is what my model looks like:

#models.py
from django.db import models

class Post(models.Model):
   title = models.CharField(max_length=100)
   slug = models.SlugField(unique=True)
   raw_body = models.TextField()
   html_body = models.TextField()
   #other random non important attributes

As you can see, I am doing a blog application. The raw_body and html_body fields are the important ones, the former one holds the unprocesses marked up text, the later holds the processed text. Since a blog is read heavy application, I opted not to run the text through the markup processed for every view. I am keeping the original so it can be edited multiple times.

#utils.py
from django.utils.html import escape
from markdown import markdown

def mark_it_down(raw):
    return markdown(escape(raw))

#models.py
from blog.utils import mark_it_down

class Post(models.Model):
    #model fields from above
    def save(self, **kwargs):
        self.html_body = mark_it_down(self.raw_body)
        super(Post, self).save(**kwargs)

The Post’s save method is overridden so that html_body can be set with the processed marked up text. I actually process the text in the mark_it_down method. This method first runs the raw text through django.utils.html.escape escape any <, >, &, “, etc with &lt;, &gt;, &amp;, &quot;, etc. Then it is run the the Markdown processor which generates the appropraite HTML. By the way, I am not displaying html_body field on the admin change/add page.

The last part is the preview with the MarkItUp! editor. If you set the previewParserPath variable in markitup/sets/markdown/set.js to a URL such as ‘/blog/markdown/preview/’, the MarkItUp will make an Ajax call to the URL with the markup text, and display the results. So I created a view to handle this:

#views.py
from django.http import HttpResponse
from moore.util import mark_it_down

def markdown_preview(request):
    processed = ''
    if request.method == 'POST':
        processed = mark_it_down(request.POST.get('data'))
    return HttpResponse(processed)

The text is in the data POST variable. The preview is displayed below the MarkItUp editor. I think this can be configured with the settings, but I haven’t played with that yet. I would like to not hard code the URL into the settings file, but then I would need to write a view for the js file and make the js file a template with the URL generated using a tag. Not hard, but I didn’t feel like doing that yet.

I believe that is everything I did, but if I missed anything or was unclear, please let me know.

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

Python 2.6 is released!

Posted in Uncategorized on October 1st, 2008 by varikin

I can’t wait to play with it and see what is new! I have been really digging Python lately, and am extremely excited.

Tags: