[Solved]-How can i customize the html output of a widget in Django?

25👍

Firstly, create a widgets.py file in an app. For my example, I’ll be making you an AdminImageWidget class that extends AdminFileWidget. Essentially, I want a image upload field that shows the currently uploaded image in an <img src="" /> tag instead of just outputting the file’s path.

Put the following class in your widgets.py file:

from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
import os
import Image

class AdminImageWidget(AdminFileWidget):
    def render(self, name, value, attrs=None):
        output = []
        if value and getattr(value, "url", None):

            image_url = value.url
            file_name=str(value)

            # defining the size
            size='100x100'
            x, y = [int(x) for x in size.split('x')]
            try :
                # defining the filename and the miniature filename
                filehead, filetail  = os.path.split(value.path)
                basename, format        = os.path.splitext(filetail)
                miniature                   = basename + '_' + size + format
                filename                        = value.path
                miniature_filename  = os.path.join(filehead, miniature)
                filehead, filetail  = os.path.split(value.url)
                miniature_url           = filehead + '/' + miniature

                # make sure that the thumbnail is a version of the current original sized image
                if os.path.exists(miniature_filename) and os.path.getmtime(filename) > os.path.getmtime(miniature_filename):
                    os.unlink(miniature_filename)

                # if the image wasn't already resized, resize it
                if not os.path.exists(miniature_filename):
                    image = Image.open(filename)
                    image.thumbnail([x, y], Image.ANTIALIAS)
                    try:
                        image.save(miniature_filename, image.format, quality=100, optimize=1)
                    except:
                        image.save(miniature_filename, image.format, quality=100)

                output.append(u' <div><a href="%s" target="_blank"><img src="%s" alt="%s" /></a></div> %s ' % \
                (miniature_url, miniature_url, miniature_filename, _('Change:')))
            except:
                pass
        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))

Ok, so what’s happening here?

  1. I import an existing widget (you may be starting from scratch, but should probably be able to extend ClearableFileInput if that’s what you are starting with)
  2. I only want to change the output/presentation of the widget, not the underlying logic. So, I override the widget’s render function.
  3. in the render function I build the output I want as an array output = [] you don’t have to do this, but it saves some concatenation. 3 key lines:
    • output.append(u' <div><a href="%s" target="_blank"><img src="%s" alt="%s" /></a></div> %s ' % (miniature_url, miniature_url, miniature_filename, _('Change:'))) Adds an img tag to the output
    • output.append(super(AdminFileWidget, self).render(name, value, attrs)) adds the parent’s output to my widget
    • return mark_safe(u''.join(output)) joins my output array with empty strings AND exempts it from escaping before display

How do I use this?

class SomeModelForm(forms.ModelForm):
    """Author Form"""
    photo = forms.ImageField(
        widget = AdminImageWidget()
    )

    class Meta:
        model = SomeModel

OR

class SomeModelForm(forms.ModelForm):
    """Author Form"""
    class Meta:
        model = SomeModel
        widgets = {'photo' : AdminImageWidget(),}

Which gives us:

admin screenshot

Leave a comment