DjangoAdmin Custom Preview Widget

Today i faced the challenge of make a custom preview from a TextField inside the admin. Now i want to share with you how i did it step by step, hope it helps…

1 – Trying a 3rd party APP …

So, first thing i did was install django-pagedown and test it to see how it works. I didn’t have any problems to install it, just checked the README and in 5 minutes i was seeing a preview in admin.

Nice, but the project i was working didn’t use markdown, instead it uses a simplest syntax. So, i go to source code from pagedown, and saw the a way to do my custom widget.

What pagedown does in python layer is basic, it just renders a custom widget with a preview textarea for the preview.

class PagedownWidget(forms.Textarea):

    def __init__(self, *args, **kwargs):
        [...]
    def _media(self):
        return forms.Media(
            css={
                "all": self.css
            },
            js=(
                compatible_staticpath("pagedown/Markdown.Converter.js"),
                compatible_staticpath('pagedown-extra/pagedown/Markdown.Converter.js'),
                compatible_staticpath("pagedown/Markdown.Sanitizer.js"),
                compatible_staticpath("pagedown/Markdown.Editor.js"),
                compatible_staticpath('pagedown-extra/Markdown.Extra.js'),
                compatible_staticpath("pagedoce wn_init.js"),
            ))
    media = property(_media)

    def render(self, name, value, attrs=None):
        template = loader.get_template(self.template)
        context = {
         [...]
        }
        context = Context(context) if VERSION < (1, 9) else context
        return template.render(context)

 

Pagedown gets a template(default.html) and renders it with some context, thats it, all the preview syntax runs in the js files. With that in mind i start doing my custom Widget:

2 – Creating my custom Widget

class AdminTextPreviewWidget(AdminTextareaWidget):
    """
    Textarea que tem um preview com a syntax do modelo
    """
    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        if "class" not in final_attrs:
            final_attrs["class"] = ""
        final_attrs["class"] += " textarea-for-preview"
        template = loader.get_template('admin/widgets/textarea_preview.html')
        context = {
            "attrs": flatatt(final_attrs),
            "body": conditional_escape(force_unicode(value)),
        }
        return template.render(Context(context))

    class Media:
        css = {
            "all": (static("admin/css/preview-widget.css"),)
        }
        js = (
            static("admin/js/preview-widget.js"),
        )

Our new template for this widget –
in templates/admin/widgets/textarea_preview.html:

{{ body }}

Preview:


</di

in admin.py:

class ModeloAdmin(admin.ModelAdmin):
    [...]
    form = FormModelo
    [...]

and my FormModelo:

class FormModelo(forms.ModelForm):
    descricao = forms.CharField(widget=AdminTextPreviewWidget(attrs={'rows':'50','cols':'100'}), required=False)
    [...]

3 – Now, what’s happening ?

I have a ModelAdmin with a custom form, and inside this form is a field with our custom widget, as the custom widget Inherits from AdminTextareaWidget, we can send attributes:

AdminTextPreviewWidget(attrs={'rows':'50','cols':'100'})

Now all we have to do is implement our custom syntax, this is happening in the JS layer.

Our preview-widget.js

$(document).ready(function($) {
   $('#id_descricao').keyup(function() {

      var string = $('#id_descricao').val();
      arrayLinha = string.split("\n");

      var pronto = ""
      for (var key in arrayLinha) {
         if (arrayLinha[key].match(/    /g)) {
            pronto += arrayLinha[key] + "<br>";
         }else{
            pronto += "<br>" + arrayLinha[key].fontsize(5).bold() + "<br>";
         }

         $('#prevCom').html(pronto);
      };
   });
});

And thats it! This is a basic syntax but it was what my project needed!

I hope it helps someone in the future….And don’t forget this is just one way to do it, and probably not the best one… but was the best for me because it was what i needed.Feel free to ask any questions .Thanks!

 

Advertisements

Author: Tiago Almeida

I am a Python / Django developer with passion about Data Science and Machine Learning.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s