Django simple admin ImageField thumbnail

The Django admin site is one of the best features of Django. It really lets you just get on with building your app, without having to worry too much about how you’ll administer your site.

The defaults are generally pretty good, but it’s often the case that you’ll want to tweak and change it (particularly when you have clients involved). Luckily it’s pretty easy to customize.

One common change that many people will want to do is to display a thumbnail of an uploaded image in the admin. The default image field (when an image has been uploaded) in the admin looks like:

image_field1

and we want to change it to look like this:

image_field2

In my case I had images that I knew would be fairly small so I didn’t need to use any auto-resizing or anything like that. I found this snippet for doing the same task, but decided to simplify it a fair bit and ended up with:


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

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)
            output.append(u' <a href="%s" target="_blank"><img src="%s" alt="%s" /></a> %s ' % \
                (image_url, image_url, file_name, _('Change:')))
        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))

Then to use it I simply override formfield_for_dbfield to return a field that uses that widget for the field I’m interested in:


class MyAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name == 'profile_image':
            request = kwargs.pop("request", None)
            kwargs['widget'] = AdminImageWidget
            return db_field.formfield(**kwargs)
        return super(MyAdmin,self).formfield_for_dbfield(db_field, **kwargs)

It’s a fairly straightforward alteration to how the ImageField widget renders in the admin, but it is often a big help to be able to actually see the image in question.

UPDATED: Feb 24th, 2010. Based on Jeremy’s comment below and having upgraded to Django 1.1 I’ve modified this example to remove the “request” param from kwargs before passing it db_field.

17 thoughts on “Django simple admin ImageField thumbnail

  1. I tried out this method, and overall it worked pretty well for me, except that I got this error when trying to go to the ‘edit’ page for a model with an image widget:

    __init__() got an unexpected keyword argument 'request'

    I found that removing ‘request’ from ‘kwargs’ fixes this, and makes the whole thing work smoothly. Eg:

    class MyAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
    if db_field.name == 'image':
    kwargs['widget'] = AdminImageWidget
    try:
    del kwargs['request']
    except KeyError:
    pass
    return db_field.formfield(**kwargs)
    return super(MyAdmin, self).formfield_for_dbfield(db_field, **kwargs)

    Thanks for sharing this snippet with us!

  2. The code ultimately needs to be used inside your apps admin.py file – when you register your models with the admin.

    The AdminImageWidget class itself can go wherever you want it and can be imported into your admin.py file(s) as needed.

  3. Hey John, nice to see you in Django land…

    Here’s a one liner that does the same job as your try/catch:

    request = kwargs.pop(‘request’, None)

  4. Hello,
    John is it?
    Thanks for the widget. it works beautifully. I’m wondering how I can get the widget to render in inline forms.

    I have an ItemImage class with a FK relationship to Items.

    It would be great if the widget would display images in the set of ItemImage change forms included on the Item change form.

    Thanks,
    Dan J.

  5. josiano,

    it should just be a case of adding the formfield_for_dbfield method to your TabularInline class definition (see Daniel’s comment).

    John

  6. I have several admin.ModelAdmin sub-classes for which I want this behaviour for all ImageField fields so I use formfield_overrides.

    class MyAdmin(admin.ModelAdmin):

    formfield_overrides = {
    models.ImageField: {‘widget’: AdminImageWidget}}

  7. Firstly thanks for this script, you got me up and going rather quickly.
    I had to tweak it to create / show a thumbnail instead of the full image. this code is a combo of yours and this: http://djangosnippets.org/snippets/955/

    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=’100×100′
    x, y = [int(x) for x in size.split('x')]

    # 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’ %s ‘ % \
    (miniature_url, miniature_url, miniature_filename, _(‘Change:’)))

    output.append(super(AdminFileWidget, self).render(name, value, attrs))
    return mark_safe(u”.join(output))

  8. Can’t get this to work. Its showing the same default stuff except the image url and the Choose button are now on the same line instead of two.

    Should I go with Francis’ widget instead?

  9. I wouldn’t be surprised if this no longer works with newer versions of Django – which version are you using?

    Francis’ code may well work better. Give it a go and see what works best.

  10. Just to report that the above widget and this Model form is merrily working in django 1.5 in Apr-2013:

    Like a charm:

    class AdminObjectForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
    super(AdminObjectForm, self).__init__(*args,**kwargs)
    self.fields['graphic'].widget = AdminImageWidget()

    Thank you!

  11. Hi!

    I got this error with Django 1.6.2:
    “object.__new__(NotImplementedType) is not safe, use NotImplementedType.__new__()”

    Thanks!

  12. Hi Mauricio,

    not sure what’s going on there – I’d need to see the full stack trace. Looks like some sort of backwards incompatible change in Django 1.6. Hardly surprisingly though, as this was originally done for Django 1.0…

    cheers,

    John

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>