Overriding a Widget in the Django Admin Site, part 2

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.

12 Responses to “Overriding a Widget in the Django Admin Site, part 2”

  1. Rick Says:

    Do you know how to configure markitup so that it doesn’t show H1 and H2? I tried to do it in set.js, but I don’t think that’s the way to do it (because it didn’t work!)

    Thanks for any help!

  2. varikin Says:

    I think it would be through editing set.js, but I haven’t tried.

  3. Jesús Gómez Says:

    I had to apply the safe filter at the html_body content in the templates. It looks like appling escape in the code is not enough.

  4. varikin Says:

    @Jesús Gómez – The safe filter servers a different need than escaping in the code. The escaping was to remove any HTML written into the raw_body before converting the markup to HTML. This prevented malicious code. Then the final html_body contains only HTML created by the markup. Then the safe filter in the template tells the template engine that it does not need to escape any further because you have already ensured it is safe.

  5. Ed Says:

    I just found this post and it solved my problems I mentioned in the comment in the previous post. Just goes to show what a little digging effort can do before automatically asking questions.

    I ended up extending this a bit by using a render_to_response with a very simple template that included my typesetting css file so that the preview ends up being typeset like it will when actually posted.

    Thanks again for this info, it has been very helpful.

  6. rick Says:

    “By the way, I am not displaying html_body field on the admin change/add page.”

    varikin, how do you hide the html_body field?

    thanks.

  7. rick Says:

    OK i found outhow to hide the html_body filed by using exclude = (‘html_body’,) in the PostAdmin class.

    But I have another issue.

    Because you escape the markdown like this:

    return markdown(escape(raw))

    When I try to display it in the template it shows escaped HTML. As far as I know there is no unescape function in django.utils, so how do you get it back to regular HTML?

    Also if you need to filter it again or process it again to unescape it back to HTML, doesn’t that add processing that is similar to using the markdown filter in the template if you had just saved it as markdown? In other words, aren’t you trading one overhead for another?

  8. rick Says:

    I just noticed that even if I remove the escape() call, it’s still escaping all the HTML. Does Django do this automatically?

  9. rick Says:

    OK. I found the safe filter, but is the safe filter much more efficient than the markdown filter?

  10. varikin Says:

    Hi Rick,

    You are busy with this:)

    I escaped the HTML because I didn’t want anything but what I generated through the markdown syntax. So I did not want to unescape it.

    The safe filter it more efficient than the markdown filter because it just tells Django “this text is already safe, you don’t have to escape anything”. Where as the markdown filter will convert markdown text to HTML when rendering the template. I haven’t done any profiling between the two, but I would hope the safe filter is faster.

    I hope this helps.

  11. rick Says:

    Thanks, varikin. About whether markdown filter’s efficiency, I guess that will be a todo for one of us :)

    If it’s not a big difference, it would be nicer to use the markdown filter, because you would only have one field in the database rather than two, which would just seem a little more elegant.

    Anyway, I think I got it all working! Thanks for the help!

  12. zeitung online Says:

    YES. You make my day. Thx. I will try to implement it in my mingus up.